ABAP OO - Sichtbarkeit, Verfügbarkeit, Datenfluss
Wie kommst du in ABAP OO an die Daten der Klasse heran und wo befinden sich gerade deine Daten in Verarbeitung? Dazu ein Beispiel in diesem Artikel.
Inhaltsverzeichnis
Bei diesem Artikel handelt es sich um Grundlagen, die du als Entwickler kennen solltest, wenn du schon länger Software entwickelst. Für einen Anfänger oder Beginner im ABAP Umfeld kann es allerdings noch recht schwer sein zu verstehen. An dieser Stelle wollen wir dir helfen.
Einleitung
In der objektorientierten Entwicklung ist es entscheidend, das richtige Design der Klasse und Variablen zu wählen. Eine Variable hat verschiedene Scopes und kann in verschiedenen Situationen verwendet werden. Nicht immer muss eine globale Variable verwendet werden, um das passende Ergebnis zu erzielen. In den folgenden Abschnitten schauen wir auf die Verwendung verschiedener Variablen und deren Fluss durch unsere Logik.
Aufbau
Das Szenario ist nun wie folgt ausgebaut. Wir haben ein Interface (ZIF_BS_DEMO_FLOW_CLASS), welches Methoden und Daten zur Verfügung stellt und welche wir in unserer Klasse verwenden.
INTERFACE zif_bs_demo_flow_class
PUBLIC.
TYPES:
BEGIN OF ts_data,
indicator TYPE i,
text TYPE string,
worked TYPE abap_boolean,
END OF ts_data,
tt_data TYPE STANDARD TABLE OF ts_data WITH EMPTY KEY.
DATA md_interface_content TYPE string.
METHODS get_magic_number
RETURNING VALUE(rd_result) TYPE i.
METHODS get_data
RETURNING VALUE(rs_result) TYPE ts_data.
ENDINTERFACE.
Die Klasse ZCL_BS_DEMO_FLOW_CLASS implementiert das Interface, damit wir die einzelnen Methoden implementieren können und eine Grundlage für die Bereitstellung haben.
CLASS zcl_bs_demo_flow_class DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_bs_demo_flow_class.
ALIASES: md_interface_content for zif_bs_demo_flow_class~md_interface_content.
CLASS-DATA gd_initialized TYPE abap_boolean.
CLASS-METHODS class_constructor.
METHODS constructor
IMPORTING id_content TYPE string.
PRIVATE SECTION.
DATA md_magic_number TYPE i.
DATA mt_data TYPE zif_bs_demo_flow_class=>tt_data.
ENDCLASS.
CLASS zcl_bs_demo_flow_class IMPLEMENTATION.
METHOD class_constructor.
gd_initialized = abap_true.
ENDMETHOD.
METHOD constructor.
md_interface_content = id_content.
md_magic_number = NEW zcl_bs_demo_random( id_min = 1
id_max = 3 )->rand( ).
mt_data = VALUE #( ( indicator = 1 text = `Line 1` )
( indicator = 2 text = `Line 2` worked = abap_true )
( indicator = 3 text = `Line 3` ) ).
mt_data[ md_magic_number ]-text =
zif_bs_demo_flow_class~md_interface_content.
ENDMETHOD.
METHOD zif_bs_demo_flow_class~get_magic_number.
rd_result = md_magic_number.
ENDMETHOD.
METHOD zif_bs_demo_flow_class~get_data.
DATA(ls_data) = mt_data[ md_magic_number ].
ls_data-worked = abap_true.
rs_result = ls_data.
ENDMETHOD.
ENDCLASS.
Für die Ausführung der Logik legen wir die Klasse ZCL_BS_DEMO_FLOW_BASE an, diese erzeugt Objekte von unserer Klasse und ruft die einzelnen Methoden auf.
CLASS zcl_bs_demo_flow_base DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_bs_demo_flow_base IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(ld_init) = zcl_bs_demo_flow_class=>gd_initialized.
out->write( |Parameter is not initial: { ld_init }| ).
DATA lo_my_flow TYPE REF TO zif_bs_demo_flow_class.
lo_my_flow = NEW zcl_bs_demo_flow_class( `New instance` ).
DATA(lo_my_new) = NEW zcl_bs_demo_flow_class( `New flow` ).
out->write( |Flow magic: { lo_my_flow->get_magic_number( ) }| ).
out->write(
|New magic: { lo_my_new->zif_bs_demo_flow_class~get_magic_number( ) }|
).
out->write( lo_my_flow->get_data( ) ).
out->write( lo_my_new->zif_bs_demo_flow_class~get_data( ) ).
ENDMETHOD.
ENDCLASS.
Wir werden den gesamten Aufbau für jedes Beispiel verwenden, hier findest du noch einmal die Klassen gegenübergestellt.
Sichtbarkeit
Die Sichtbarkeit gilt für alle Inhalte innerhalb des Abschnitts, hier können Typen, Konstanten, Variablen oder Methoden stehen. Die Sichtbarkeit bestimmt, wer auf die Inhalte zugreifen kann.
- PUBLIC - Jeder kann auf den Inhalt zugreifen.
- PROTECTED - Die Klasse selbst und die Unterklassen können auf die Inhalte zugreifen.
- PRIVATE - Nur die aktuelle Klasse kann auf die Inhalte zugreifen.
Scope
Im ersten Schritt schauen wir uns den Scope von Variablen an, wo sie sichtbar sind und wie du sie nutzen kannst.
Global
Globale Elemente in der Klasse erkennst du am Zusatz CLASS, wie zum Beispiel bei CLASS-DATA oder CLASS-METHOD. Diese Elemente gehören zu allen Instanzen der Klasse und eignen sich sehr gut für die Pufferung von Daten oder die Verwaltung des Singleton für die Klasse. Bei letzterem handelt es sich um ein Design Pattern in der objektorientierten Entwicklung. In diesem Beispiel wollen wir auf die Variable GD_INITIALIZED zugreifen. Die Variable wurde ohne Inhalt definiert und sollte zu Beginn entsprechend leer sein.
Dazu die folgenden Schritte:
- Erster Zugriff auf die Klasse, damit wird der Klassen-Konstruktor aufgerufen. Dieser wird nur einmalig, beim ersten Zugriff, aufgerufen und in den weiteren Schritten nicht mehr.
- Einmalige Ausführung des Klassen-Konstruktors, wir haben an dieser Stelle Zugriff auf alle Methoden und Daten, die mit CLASS in der Klasse definiert wurden (Global, Statisch).
- Setzen der Variable GD_INITIALIZED auf ABAP_TRUE, damit ist die Variable nun befüllt.
- Da die Variable Public ist, können wir über den Klassennamen auf die Variable zugreifen und unserer lokalen Variable den Inhalt zuweisen. In LD_INIT befindet sich nun auch der Wert ABAP_TRUE.
Instanz
Eine Instanz-/Member-Variable steht nur einer erzeugten Instanz einer Klasse zur Verfügung, egal ob sie privat oder öffentlich ist. Innerhalb der Instanz sind die Variablen global und stehen jeder Methode zur Verfügung. Hier sollten aber nur Daten gehalten werden, die auch wirklich global sein müssen.
Dazu die folgenden Schritte:
- Erzeugung einer neuen Instanz der Klasse ZCL_BS_DEMO_FLOW_CLASS. Wurde bereits der Klassen-Konstruktor durchlaufen, wird nun als Erstes der normale Konstruktor durchlaufen.
- Im Konstruktor haben wir Zugriff auf Klassen-Methoden und Attribute, sowie alle Instanz-/Member-Variablen und normalen Methoden. Oft wird der Konstruktor verwendet, um die Daten innerhalb der Klasse zu initialisieren. Du solltest aber vermeiden, langlaufende Aktionen im Konstruktor zu erledigen, wie das Laden von Daten von der Datenbank (Lazy Loading).
- Im Konstruktor wird bereits unser Feld MD_MAGIC_NUMBER mit einer zufälligen Zahl befüllt. Alle Methoden der Klasse haben Zugriff auf die Variable.
Lokal
Zum Abschluss schauen wir uns die lokalen Variablen an, diese können nur innerhalb der Methode verwendet werden, wo sie auch definiert werden. Nachdem die Methode verlassen wurde, wird die Variable und der Inhalt wieder aus dem Speicher entfernt.
Dazu die folgenden Schritte:
- Lesen der Tabelle MT_DATA und Erzeugung von LS_DATA per Inline-Deklaration.
- Zugriff und Änderung der Daten in LS_DATA innerhalb der Methode
- Übergabe der Inhalte aus LS_DATA an RS_RESULT.
Datenfluss
Die wichtigsten Parameter in der Entwicklung sind Importing und Returning Parameter. Mit Importing bekommen wir Werte und Inhalte in die Methode und mit Returning geben wir das Ergebnis wieder zurück.
Importing
In diesem Beispiel schauen wir uns an, was mit dem Inhalt aus dem Konstruktor passiert, den wir an die Klasse übergeben. Dabei gehen wir auch auf einige spezielle Konzepte in ABAP ein.
Dazu die folgenden Schritte:
- Übergabe des String-Literals an den Konstruktor der Klasse. Da wir nur einen Importing Parameter haben, können wir die Zuweisung weglassen.
- Wir können auf den Inhalt über ID_CONTENT innerhalb der Methode zugreifen. Den Inhalt weisen wir der Membervariable MD_INTERFACE_CONTENT zu.
- Die Zuweisung ist nur ohne Interface möglich, da wir einen ALIAS definiert haben, der auf die Variable im Interface verweist.
- Die Variable, die wir im Interface definieren, steht auch in der Klasse zur Verfügung und kann genutzt werden, auch wenn wir sie nicht noch einmal explizit definiert haben.
- Alternativ können wir auch immer noch über das Interface auf die Variable zugreifen. In diesem Fall übernehmen wir den Inhalt in die Tabellenzeile der zufälligen Position.
Returning
Jede Methode kann nur genau einen Returning Parameter besitzen. Du solltest dich daher beim Design der Methode für Exporting, Changing oder Returning entscheiden, eine Mischung ist nicht unbedingt ideal. Ein Returning Parameter muss nicht angegeben werden, sondern der Aufruf der Methode gibt als Ergebnis den Returning Wert zurück.
Dazu die folgenden Schritte:
- Wir rufen die Methode GET_DATA auf, diese ist im Interface definiert und in der Klasse implementiert.
- Wir greifen auf die beiden Member MT_DATA und MD_MAGIC_NUMBER zu, diesen stehen der Methode zur Verfügung.
- Wir definieren eine lokale Variable der Zeile und setzen die Spalte "Worked" auf ABAP_TRUE.
- Die lokale Variable wird RS_RESULT zugewiesen, unserem Returning Parameter der Methode. Alternativ kannst du auch "RETURN ls_data" verwenden, wenn du in der BTP oder S/4 HANA 2023 bist. Der Aufruf der Method gibt das Ergebnis zurück, welches wir direkt der WRITE Methode übergeben.
Fazit
Ziel des Artikels ist es, dir die verschiedenen Bereich der Klassen näher zu bringen und wie sie miteinander interagieren. Mehr Informationen zu gutem objektorientierten Design findest du im Clean ABAP Guide.