ABAP OO - Dynamisch, Statisch, Super
In diesem Artikel wollen wir dir einmal den Unterschied von dynamischen und statischen Klassen zeigen und was du damit machen kannst. Weiterhin schauen wir uns die verschiedenen Konstruktoren an.
Inhaltsverzeichnis
Im letzten Artikel sind wir vor allem auf die verschiedenen Bestandteile der Klasse eingegangen und haben uns eine einfache Vererbung angeschaut. Hier gehen wir etwas mehr in die Tiefe, wie man mit Klassen arbeitet und was die Unterschiede beim Zugriff sind.
Statisch
Statische Bestandteile einer Klasse können immer ohne Instanz verwendet werden und gehören direkt zur Klasse, darunter zählen:
- Typen
- Konstanten
- Statische Attribute
- Statische Methoden
Um auf einen statischen Bestandteil zugreifen zu können, wir der "Fat Arrow" verwendet, direkt nach Angabe der Klasse. Dazu ein kleines Beispiel einer Klasse mit allen Definitionen, wie dir sicherlich auffällt, bekommt die Datendefinition und die Methode den Zusatz CLASS. Mit diesem Zusatz ist der Zusammenhang zur Klasse gegeben.
CLASS lcl_example DEFINITION.
PUBLIC SECTION.
TYPES:
td_number TYPE p LENGTH 15 DECIMALS 2.
CONSTANTS:
c_magic_number TYPE td_number VALUE '13.37'.
CLASS-DATA:
gd_save TYPE abap_bool.
CLASS-METHODS:
is_number_magic
IMPORTING
id_number TYPE td_number
RETURNING VALUE(rd_result) TYPE abap_bool.
ENDCLASS.
Nachdem wir die Definition festgelegt haben können wir nun auf die Bestandteile zugreifen, ohne eine Instanz von der Klasse zu erzeugen:
DATA:
ld_one_number TYPE lcl_example=>td_number.
IF lcl_example=>gd_save = abap_true.
ld_one_number = lcl_example=>c_magic_number.
DATA(ld_magic_flag) = lcl_example=>is_number_magic( ld_one_number ).
ENDIF.
Wie du siehst kann der Typ als Referenz genommen werden, ebenso kann auf die verschiedenen Komponenten innerhalb der Klasse zugegriffen werden. Wenn dein Klassenname sehr lang ist, dann werden auch entsprechend deine Implementierungen länger.
Dynamisch
Dynamische Eigenschaften einer Klasse sind Attribute und Methoden die nur mit einer Instanz genutzt werden können. Bevor du also auf die verschiedenen Komponenten zugreifen kannst, musst du zuvor eine Instanziierung durchführen. Dazu definieren wir die folgende Klasse:
CLASS lcl_example DEFINITION.
PUBLIC SECTION.
TYPES:
tt_codes TYPE STANDARD TABLE OF t001 WITH EMPTY KEY.
DATA:
md_name TYPE string.
METHODS:
select_company_codes
RETURNING VALUE(rt_codes) TYPE tt_codes.
ENDCLASS.
Um nun auf die zugreifen zu können, erzeugen wir eine Instanz mit NEW und können diese dann verwenden. Der Zugriff erfolgt über den einfachen Pfeil, wie man sonst auch auf Referenzen zugreift.
DATA(lo_instance) = NEW lcl_example( ).
lo_instance->md_name = `John Doe`.
DATA(lt_codes) = lo_instance->select_company_codes( ).
Auf öffentliche Variablen können wir jederzeit zugreifen und wenn sie nicht geschützt sind, auch neuen Inhalt zuweisen. Einen öffentlichen Typen in einer Klasse können wir auch für eine Datendeklaration außerhalb der Klasse verwenden. Hier spielen statische Typen mit dynamischen Methoden zusammen.
Dynamisch vs Statisch
Für was benötigt man nun die beiden Varianten einer Klasse? Dazu wollen wir dir erst einmal die Vor- und Nachteile dieser Varianten aufzeigen, damit dies grob einordnen kannst.
Statisch
Eine statische Klasse verhält sich wie eine Funktionsgruppe, die Daten in der Klasse sind die ganze Session über verfügbar und man benötigt dafür keine Instanz. Dafür kann man nur einmal Daten in der Klasse sichern und hat das Risiko das die Daten noch verfübar sind, wenn man davon ausgeht, dass die Klasse eigentlich initialisert sein sollte.
Dynamisch
Für dynamische Objekte benötigst du zuerst einmal eine Instanz, die du erstellen musst, bevor du die Attribute und Methoden zugreifen kannst. Du kannst diese Instanz aber zum Beispiel in einer Tabelle sichern und weitere Instanzen erzeugen. Die Daten sind in jeder Instanz für sich und wenn die Referenz auf das Objekt gelöscht wird, dann werden alle Daten sauber gelöscht.
Verwendung
Im Normalfall solltest du die dynamische Variante implementieren, da sie neben Wiederverwendbarkeit auch eine saubere Speicherverwaltung bietet. Damit wirst du im System mehr Varianten dieser Form finden, wobei statische Objekte nicht obsolet sind. Sie werden vor allem für viele Hilfsmethoden genutzt und bieten die Möglichkeit über Objekte hinweg mit Puffern in der Klasse zu arbeiten.
Es empfiehlt sich beim Klassendesign nur eine der beiden Varianten in einer Klasse zu verwenden und Mischformen zu vermeiden, da andere Kollegen bei der Verwendung sonst Probleme bekommen könnten, da sie die einzelnen Methoden nicht kennen.
Konstruktor
Eine Spezialität in ABAP ist, dass es zwei Konstruktoren gibt, die für unterschiedliche Dinge verwendet werden können. Der Konstruktor im allgemeinen ist eine Methode der Klasse, die bei Erstellung der Instanz oder der ersten Nutzung der Klassenkomponenten automatisch aufgerufen wird. Er erstellt das Objekt und initialisiert die Variablen oder lädt Daten von der Datenbank, um so die Instanz für die Nutzung vorzubereiten.
CLASS_CONSTRUCTOR
So gibt es für den statischen Teil der Klasse den CLASS_CONSTRUCTOR. Dieser wird immer dann aufgerufen, wenn die Klasse oder eine Komponente dieser, aufgerufen wird. Wie der Name bereits vermuten lässt, wird sie als statische Methode implementiert. Dazu wieder eine Beispielklasse mit der Implementierung:
CLASS lcl_static DEFINITION.
PUBLIC SECTION.
CLASS-DATA:
gd_magic_number TYPE i.
CLASS-METHODS:
class_constructor.
ENDCLASS.
CLASS lcl_static DEFINITION.
METHOD class_constructor.
gd_magic_number = gd_magic_number + 1.
ENDMETHOD.
ENDCLASS.
Als nächstes geben wir den Wert der Variable drei Mal über die WRITE Anweisung aus. Der Klassenkonstruktor wird nur einmal durchlaufen und der Wert der Zahl auf 1 erhöht. Solange wir den Wert in der Variable nicht ändern, wird dieser immer konstant verfügbar sein.
" Output will always be 1
WRITE: / lcl_static=>gd_magic_number.
WRITE: / lcl_static=>gd_magic_number.
WRITE: / lcl_static=>gd_magic_number.
Hinweis: Der Klassenkonstruktor kann keine Import-Parameter haben und keine Ausnahmen auslösen, wenn er nicht in der Klasse implementiert wird, dann ist es eine leere Methode.
CONSTRUCTOR
Der Konstruktor wird immer dann durchlaufen, wenn eine neue Instanz von einem Objekt erzeugt wird, entweder über NEW oder CREATE OBJECT. Wir empfehlen die Nutzung von NEW, da es der neue Standard ist. Wenn du mit dynamischen Typen zur Laufzeit arbeiten willst, musst du mit CREATE OBJECT arbeiten.
Definieren wir als Beispiel eine Klasse mit einem Konstruktor der eine Zahl entgegen nimmt:
CLASS lcl_dynamic DEFINITION.
PUBLIC SECTION.
DATA:
md_magic_number TYPE i.
METHODS:
constructor
IMPORTING
id_number TYPE i.
ENDCLASS.
CLASS lcl_dynamic DEFINITION.
METHOD constructor.
md_magic_number = id_number.
ENDMETHOD.
ENDCLASS.
In dem nächsten Beispiel erzeugen wir eine Instanz der Klasse und geben über den Konstruktor eine Zahl mit, die intern die Variable "md_magic_number" initialisiert. Bei der Ausgabe hat die Variable also immer den Wert, den wir dem Konstruktor, bei Erzeugung, übergeben haben.
" Output will always be the constructor value
DATA(lo_instance) = NEW lcl_dynamic( 13 ).
WRITE: / lo_instance->md_magic_number.
Hinweis: Der Konstruktor kann Import-Parameter haben und auch Ausnahmen werfen, wenn es zu einem Fehler kommt. In solchen Fällen wird die Instanziierung abgebrochen.
Super
Es ist super das du ABAP OO in der Tiefe lernen willst, aber das erklärt natürlich nicht, was "super" wirklich macht. Hierbei handelt es sich um eine Pseudoreferenz, wenn du einmal in einer Vererbung auf die Oberklasse zugreifen willst. In manchen Fällen musst du das sogar machen, da es sonst zu einem Fehler kommt. Dazu definieren und implementieren wir wieder eine Klasse, die auf der dynamischen Klasse des letzten Abschnitts aufbaut.
CLASS lcl_super DEFINITION INHERITING FROM lcl_dynamic.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING
id_number TYPE i.
ENDCLASS.
CLASS lcl_super DEFINITION.
METHOD constructor.
super->constructor( id_number ).
md_magic_number = md_magic_number + 1.
ENDMETHOD.
ENDCLASS.
Wir müssen den Konstruktor der Oberklasse aufrufen, da es sonst zu einem Fehler kommt und die Logik in der Oberklasse zur Initialisierung der Klasse nicht durchlaufen wird und auch die Importing-Parameter nicht versorgt werden. Der super-Aufruf wird überall dort benötigt, wo du eine gleichnamige Methode aufrufen willst, wenn du diese zum Beispiel in der Untermethode redefiniert hast.
Fazit
Heute ging es einmal um die große Frage ob dynamische oder statische Klassen verwendet werden sollten, dabei hat jede der beiden Formen ihr Vor- und Nachteile und können effektiv auch zusammen verwendet werden. Wichtig ist auch die Verwendung der Konstruktoren, da bei vielen Klassen zu beginn bereits eine Initialisierung durchgeführt werden soll.