ABAP OO - Methodenparameter
In diesem Artikel einmal die Grundlagen der Parameter in Methoden und wie sich diese bei verschiedenen Typen verhalten.
Inhaltsverzeichnis
ABAP OO sieht man in vielen Stellenausschreibungen als Standard Buzzword, wenn es um die Anforderungen an einen ABAP Entwickler geht. Doch in der Praxis stellt man immer wieder fest, dass Entwickler die Grundlagen zur Erstellung von sauberen Objekten fehlt. In diesem Artikel geht es einmal um die Grundlagen der Methodenparameter, wie du sie einsetzen kannst und was du damit tun kannst.
Einleitung
Die Parameter werden zum Austausch von Informationen in deiner Klasse verwendet und diese trifft man sehr häufig in Klassen an. Werden in deiner Klasse viel häufiger Attribute verwendet, solltest du dir noch einmal Gedanken machen, ob wirklich alle Informationen in Attributen stehen müssen oder diese über die Parameter von Methode zu Methode weitergegeben werden sollten.
In unserem Beispiel werden wir eine Klasse verwenden, die sehr einfach aufgebaut ist. Sie besitzt ein Attribut, welches wir über einen GETTER und SETTER bearbeiten können. Weiterhin wird über den Konstruktor der Initalwert festgelegt:
CLASS zcl_bs_demo_object DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS constructor
IMPORTING id_field TYPE string.
METHODS get_field
RETURNING VALUE(rd_result) TYPE string.
METHODS set_field
IMPORTING id_field TYPE string.
PRIVATE SECTION.
DATA md_field TYPE string.
ENDCLASS.
CLASS zcl_bs_demo_object IMPLEMENTATION.
METHOD constructor.
md_field = id_field.
ENDMETHOD.
METHOD get_field.
rd_result = md_field.
ENDMETHOD.
METHOD set_field.
md_field = id_field.
ENDMETHOD.
ENDCLASS.
Grundlage
Für eine Methode stehen dir verschiedene Typen von Parametern zur Verfügung, die sich unterschiedlich durch ihre Schlüsselworte abgrenzen lassen:
- IMPORTING - Über diesen Parameter werden Inhalte in die Methode gegeben.
- EXPORTING - Über diesen Parameter werden Werte zurückgegeben.
- CHANGING - Dieser Parameter übernimmt die Funktion von IMPORTING und EXPORTING, bekommt also Werte in die Methode, kann diese aber auch anpassen und nach außen geben.
- RETURNING - Gibt genau einen Wert/Objekt aus der Methode zurück, davon kann es nur einen Parameter pro Methode geben.
VALUE oder REFERENCE
Wenn du einen Parameter definierst, stehen dir aktuell zwei Wege zur Verfügung, einmal als VALUE Übergabe. Dabei wird eine Kopie des Variableninhalts zur Verfügung gestellt:
IMPORTING VALUE(id_value) TYPE string
Der andere Weg ist als REFERENCE, dabei wird, wie der Name schon sagt, eine Referenz des Wertes übergeben. Dabei gibt es aktuell zwei Schreibweisen, einmal mit dem Zusatz wie bei Value und als zweite Variante ohne Zusatz, damit wird die Variable automatisch als Referenz behandelt.
" Without addition
IMPORTING id_value TYPE string
" With addition
IMPORTING REFERENCE(id_value) TYPE string
Hinweis: Bei RETURNING Parametern gibt es nur den Zusatz VALUE.
Verhalten
In diesem Abschnitt wollen wir uns einmal das Verhalten der Parameter in drei verschiedenen Szenarien und jeweils drei verschiedenen Ausprägungen anschauen. Dabei verwenden wir einen elementaren Datentyp, eine Referenz und eine Instanz und das für Importing, Exporting und Changing. Dabei übergeben wir die Daten einmal als Value und Reference und versuchen die Inhalte zu Ändern. Im letzten Schritt rufen wir einfach die Methode auf, aber führen keine Änderungen an den Inhalten durch.
Führen wir einmal die Beispielklasse aus, dann erhalten wir die folgende Ausgabe. Das Beispiel findest du im unteren Abschnitt des Artikels:
Vorn findest du die jeweilige Kategorie, in den Klammern das Szenario und dahinter die Werte der jeweiligen Typen.
- Kategorie
- Values - Elementarer Datentyp
- References - Referenz auf Datentyp
- Object - Instanz eines Objekts
- Szenario
- def - Initialisierte Werte
- byval - Übergabe per VALUE
- byref - Übergabe per REFERENCE
- unchg - Unveränderte Werte
- Typen
- I - Importing
- E - Exporting
- C - Changing
IMPORTING
Importing Parameter geben Werte und Inhalte in die Methode, sollten aber laut Definition nicht änderbar sein. Bei der Übergabe als elementarer Datentyp (REFERENCE) erhalten wir direkt auch einen Fehler vom Compiler, wenn wir einen neuen Wert zuweisen wollen, und können den Quellcode nicht aktivieren.
Bei der Übergabe als VALUE können wir in der Methode mit der Variable arbeiten und sogar den Wert überschreiben. Dies gilt aber nur für die aktuelle Methode, denn sobald wir diese Verlassen, hat die Übergabe wieder den alten Wert. Wir haben nur eine "Kopie" der Variable erhalten.
Bei den Referenzen (TYPE REF) verhält es sich allerdings komplett anders, egal ob Importing Parameter oder Exporting Parameter, wenn wir den Inhalt in der Methode ändern, ändert sich auch unsere Variable außerhalb der Methode.
EXPORTING
Exporting Parameter sind dazu gedacht, Ergebnisse an den Aurufer zurückzugeben. Arbeiten wir per REFERENCE, erhalten wir aber auch den Wert der aufnehmenden Variable in die Methode, wenn dieser bereits befüllt wurde.
Übergeben wir die Werte per VALUE, dann sind die Variablen leer und wir arbeiten mit initialen Variablen.
Vorsicht ist allerdings geboten, wenn wir die Variablen per Reference in die Methode geben. Überschreiben oder Löschen wir die Werte nicht, bleibt der alte Wert in der Variable stehen, was zu späteren Fehlern in der Verarbeitung führen kann. Hier lohnt es sich bereits auf den richtigen Typ der Übergabe zu setzen oder die Exporting Parameter am Anfang der Methode zu löschen.
CHANGING
Der Changing Parameter ist in diesem Fall der flexibelste und verhält sich in allen Situationen gleich, wir bekommen den Wert von außen in die Methode, können diesen Ändern oder so belassen und geben den Inhalt wieder an den Aufrufer zurück.
Beispiel
Hier einmal die Komplette Beispielklasse die wir verwendet haben, diese Klasse ist eine ausführbare Klasse mit MAIN Methode zum Start in Eclipse.
CLASS zcl_bs_demo_oo_methods DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
TYPES: BEGIN OF ts_value,
import TYPE string,
export TYPE string,
change TYPE string,
END OF ts_value.
TYPES: BEGIN OF ts_reference,
import TYPE REF TO string,
export TYPE REF TO string,
change TYPE REF TO string,
END OF ts_reference.
TYPES: BEGIN OF ts_object,
import TYPE REF TO zcl_bs_demo_object,
export TYPE REF TO zcl_bs_demo_object,
change TYPE REF TO zcl_bs_demo_object,
END OF ts_object.
METHODS by_reference
IMPORTING id_value TYPE string
ir_reference TYPE REF TO string
io_object TYPE REF TO zcl_bs_demo_object
EXPORTING ed_value TYPE string
er_reference TYPE REF TO string
eo_object TYPE REF TO zcl_bs_demo_object
CHANGING cd_value TYPE string
cr_reference TYPE REF TO string
co_object TYPE REF TO zcl_bs_demo_object.
METHODS by_value
IMPORTING VALUE(id_value) TYPE string
VALUE(ir_reference) TYPE REF TO string
VALUE(io_object) TYPE REF TO zcl_bs_demo_object
EXPORTING VALUE(ed_value) TYPE string
VALUE(er_reference) TYPE REF TO string
VALUE(eo_object) TYPE REF TO zcl_bs_demo_object
CHANGING VALUE(cd_value) TYPE string
VALUE(cr_reference) TYPE REF TO string
VALUE(co_object) TYPE REF TO zcl_bs_demo_object.
METHODS unchanged
IMPORTING id_value TYPE string
ir_reference TYPE REF TO string
io_object TYPE REF TO zcl_bs_demo_object
EXPORTING ed_value TYPE string
er_reference TYPE REF TO string
eo_object TYPE REF TO zcl_bs_demo_object
CHANGING cd_value TYPE string
cr_reference TYPE REF TO string
co_object TYPE REF TO zcl_bs_demo_object.
METHODS output
IMPORTING io_out TYPE REF TO if_oo_adt_classrun_out
id_scenario TYPE string
is_value TYPE ts_value
is_reference TYPE ts_reference
is_object TYPE ts_object.
ENDCLASS.
CLASS zcl_bs_demo_oo_methods IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA ls_value TYPE ts_value.
DATA ls_reference TYPE ts_reference.
DATA ls_object TYPE ts_object.
ls_value = VALUE #( import = '1'
export = '1'
change = '1' ).
ls_reference = VALUE #( import = NEW #( '1' )
export = NEW #( '1' )
change = NEW #( '1' ) ).
ls_object = VALUE #( import = NEW #( '1' )
export = NEW #( '1' )
change = NEW #( '1' ) ).
output( io_out = out
id_scenario = 'def'
is_value = ls_value
is_reference = ls_reference
is_object = ls_object ).
by_reference( EXPORTING id_value = ls_value-import
ir_reference = ls_reference-import
io_object = ls_object-import
IMPORTING ed_value = ls_value-export
er_reference = ls_reference-export
eo_object = ls_object-export
CHANGING cd_value = ls_value-change
cr_reference = ls_reference-change
co_object = ls_object-change ).
output( io_out = out
id_scenario = 'byref'
is_value = ls_value
is_reference = ls_reference
is_object = ls_object ).
by_value( EXPORTING id_value = ls_value-import
ir_reference = ls_reference-import
io_object = ls_object-import
IMPORTING ed_value = ls_value-export
er_reference = ls_reference-export
eo_object = ls_object-export
CHANGING cd_value = ls_value-change
cr_reference = ls_reference-change
co_object = ls_object-change ).
output( io_out = out
id_scenario = 'byval'
is_value = ls_value
is_reference = ls_reference
is_object = ls_object ).
unchanged( EXPORTING id_value = ls_value-import
ir_reference = ls_reference-import
io_object = ls_object-import
IMPORTING ed_value = ls_value-export
er_reference = ls_reference-export
eo_object = ls_object-export
CHANGING cd_value = ls_value-change
cr_reference = ls_reference-change
co_object = ls_object-change ).
output( io_out = out
id_scenario = 'unchg'
is_value = ls_value
is_reference = ls_reference
is_object = ls_object ).
ENDMETHOD.
METHOD by_reference.
DATA ld_value TYPE string VALUE `Changed`.
* id_value = ld_value.
ir_reference->* = ld_value.
io_object->set_field( ld_value ).
ed_value = ld_value.
er_reference->* = ld_value.
eo_object->set_field( ld_value ).
cd_value = ld_value.
cr_reference->* = ld_value.
co_object->set_field( ld_value ).
ENDMETHOD.
METHOD by_value.
DATA ld_value TYPE string VALUE `Again`.
id_value = ld_value.
ir_reference->* = ld_value.
io_object->set_field( ld_value ).
ed_value = ld_value.
er_reference = NEW #( ld_value ).
eo_object = NEW #( ld_value ).
cd_value = ld_value.
cr_reference->* = ld_value.
co_object->set_field( ld_value ).
ENDMETHOD.
METHOD unchanged.
ENDMETHOD.
METHOD output.
io_out->write( |Values ({ id_scenario }) ... I: { is_value-import }; E: { is_value-export }; C: { is_value-change }| ).
io_out->write(
|Reference ({ id_scenario }) ... I: { is_reference-import->* }; E: { is_reference-export->* }; C: { is_reference-change->* }| ).
io_out->write(
|Object ({ id_scenario }) ... I: { is_object-import->get_field( ) }; E: { is_object-export->get_field( ) }; C: { is_object-change->get_field( ) }| ).
io_out->write( `-` ).
ENDMETHOD.
ENDCLASS.
Fazit
Die Verwendung der verschiedenen Parametern gehört für jeden ABAP Entwicklung zum Standard, wobei mittlerweile durch Clean ABAP die "beste" Methode nur einen bis wenige Importing- und einen Returning Parameter haben sollte. Mit solchen Methoden lässt sich sehr leicht arbeiten, diese sind aber nicht immer so einfach umsetzbar.