![ABAP OO Redefinition, Setter, Getter](/user_content/content/abap-oo-redefinition-setter-getter.png)
ABAP OO - Redefinition und Getter/Setter
In diesem Artikel schauen wir uns das Thema Redefinition an und wie dir Getter und Setter bei einheitlichen Schnittstellen helfen.
Inhaltsverzeichnis
Heute geht es einmal um die Redefinition und wie sie dir hilft bessere Klassen zu bauen und Code effizienter zu strukturieren. Als zweites Thema schauen wir uns die Verwendung von Getter und Setter Methoden an und wie sie deine Schnittstellen besser machen.
Redefinition
Die Redefinition gehört zur Vererbung von Klassen und kann eingesetzt werden, um geerbete Methoden zu überschreiben und ihnen damit neue Logik zur Verfügung zu stellen. Wichtig dabei ist, dass sich der Name der Methode und die Schnittstelle nicht ändern. Diese bleiben weiterhin stabil und es wird nur eine neue Ablauflogik implementiert. Damit bleibt bei Übergabe an eine andere Schnittstelle das Objekt stabil, die Ausgabe und die Daten können sich aber entsprechend ändern.
Dazu definieren wir uns eine einfache Klasse die eine Berechnung durchführen soll. Die Methode nimmt eine Tabelle mit Zahlen entgegen und gibt uns die entsprechende Summe zurück. An dieser Stelle solltest du beachten, dass deine Klasse nicht FINAL ist, da du sonst nicht mehr von ihr erben kannst.
CLASS zcl_bs_demo_calculator DEFINITION PUBLIC CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
ts_numbers TYPE i,
tt_numbers TYPE STANDARD TABLE OF ts_numbers WITH EMPTY KEY.
METHODS:
calculate
IMPORTING
it_numbers TYPE tt_numbers
RETURNING VALUE(rd_result) TYPE i.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_calculator IMPLEMENTATION.
METHOD calculate.
LOOP AT it_numbers INTO DATA(ld_number).
rd_result += ld_number.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Nun möchten wir eine Klasse implementieren, die die gleichen Schnittstellen hat, wie die erste Klasse, aber die Berechnung etwas anders läuft. In diesem Fall definieren wir eine neue Klasse und erben von unserer ersten Klasse. Nun musst du nur noch die Methode CALCULATE redefinieren und kannst dann die Logik neu implementieren. Die Schnittstelle bleibt dabei stabil und kann nicht verändert werden. Damit du die Methode lokal neu implementieren kannst, musst du die Methode in der Klasse anlegen und mit dem Schlüsselwort REDEFINITION die Methode überschreiben. Im Anschluss kannst die die implementierung neu vornehmen.
CLASS zcl_bs_demo_calc_redefinition DEFINITION PUBLIC CREATE PUBLIC
INHERITING FROM zcl_bs_demo_calculator.
PUBLIC SECTION.
METHODS:
calculate REDEFINITION.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_calc_redefinition IMPLEMENTATION.
METHOD calculate.
rd_result = 1.
LOOP AT it_numbers INTO DATA(ld_number).
rd_result *= ld_number.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Nun können wir die beiden Klassen testen. Dazu definieren wir eine Konsolen Applikation und definieren die Referenz auf Basis der Originalklasse und befüllen die Zahlen mit 1-5, um eine Grundlage für die Berechnung zu haben. Im Anschluss erzeugen wir eine Instanz der Klasse und geben das Ergebnis in die Konsole aus.
CLASS zcl_bs_demo_calc_usage DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_calc_usage IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
lo_calculator TYPE REF TO zcl_bs_demo_calculator.
DATA(lt_numbers) = VALUE zcl_bs_demo_calculator=>tt_numbers(
( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 )
).
lo_calculator = NEW zcl_bs_demo_calculator( ).
out->write( |Original class: { lo_calculator->calculate( lt_numbers ) }| ).
lo_calculator = NEW zcl_bs_demo_calc_redefinition( ).
out->write( |Redefined class: { lo_calculator->calculate( lt_numbers ) }| ).
ENDMETHOD.
ENDCLASS.
In dem Beispiel verwenden wir die selbe Referenzvariable, um die stabile Schnittstelle zu simulieren und erstellen eine Instanz der Klasse, bevor wir die CALCULATE Methode aufrufen. Hier siehst du das Ergebnis der Berechnung, die beiden unterschiedlich definierten Methoden wurden aufgerufen.
Getter/Setter
Die meisten Klassen besitzen auch Attribute die innerhalb der Klasse genutzt werden, aber auch Daten nach Außen bereitstellen. Solche Attribute kann man als PUBLIC definieren und sie somit über das Objekt verfügbar machen. Damit kann ein Nutzer jederzeit auf das Attribut zugreifen, die Daten lesen, aber auch Ändern. Dieses Verhalten ist nicht immer gewünscht und hat einen entscheidenden Nachteil, du hast keinen Einfluss mehr auf das Atrribut, bevor es nach Außen gegeben wird und dieses muss jederzeit stabil bleiben (Datentyp).
Schauen wir uns dazu einmal ein kleines Beispiel einer Klasse an, diese besitzt eine Tabelle die Nachrichten aufnimmt, die über ADD_MESSAGE hinzugefügt werden.
CLASS zcl_bs_demo_public_data DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
DATA:
mt_messages TYPE string_table.
METHODS:
add_message
IMPORTING
id_message TYPE string.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_public_data IMPLEMENTATION.
METHOD add_message.
INSERT id_message INTO TABLE mt_messages.
ENDMETHOD.
ENDCLASS.
In diesem fiktiven Beispiel fügen wir verschiedene Meldungen der Klasse hinzu und mitten in diesem Prozess löschen wir die Meldungen. Dies soll simulieren, dass während unser Ausführung ein anderes Stück Quellcode die Klasse initialisiert hat. Alle Meldungen bis dahin sind verloren und das Ergebnis könnte verfälscht werden.
DATA(lo_public_data) = NEW zcl_bs_demo_public_data( ).
lo_public_data->add_message( `Message 1` ).
CLEAR lo_public_data->mt_messages.
lo_public_data->add_message( `Message 2` ).
lo_public_data->add_message( `Message 3` ).
Um dies zu verhindern, können wir die Sichtbarkeit des Attributes verändern und es auf PROTECTED oder PRIVATE setzen. Damit kann es von Außen nicht mehr verändert werden und unsere Nachrichten sind sicher vor ungewollter Veränderung. Wie kommen wir nun von Außen an die Nachrichten ran? Dazu implementieren wir einen Getter, dies ist eine Methode die mit GET_ beginnt, meist danach den Namen des Atrributs hat und einen Returning Parameter besitzt, der das Attribut zurückgibt. Die geänderte Klasse könnte nun wie folgt aussehen:
CLASS zcl_bs_demo_private_data DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
add_message
IMPORTING
id_message TYPE string,
get_messages
RETURNING VALUE(rt_result) TYPE string_table.
PROTECTED SECTION.
PRIVATE SECTION.
DATA:
mt_messages TYPE string_table.
ENDCLASS.
CLASS zcl_bs_demo_private_data IMPLEMENTATION.
METHOD add_message.
INSERT id_message INTO TABLE mt_messages.
ENDMETHOD.
METHOD get_messages.
rt_result = mt_messages.
ENDMETHOD.
ENDCLASS.
Unsere Nachrichten MT_MESSAGES sind nun geschützt gegen ungewollte Veränderungen. Ein Setter ist dagegen eine Methode die mit SET_ beginnt, gefolgt von dem Namen des Attributes und einen Importing Parameter hat. Diese Methode setzt das Attribut in der Klasse mit einem neuen Wert.
Getter und Setter haben verschiedene Vorteile bei der Verwendung:
- Implementierung von zusätzlichem Prüf- und Filtercode
- Schutz der Attribute vor ungewollten Änderungen
- Einheitliche und stabile Schnittstellen
- Möglichkeiten in den Prozess einzugreifen
Read Only
Neben den Getter und Setter Methoden gibt es auch noch eine andere Möglichkeit mit öffentlichen Attributen zu arbeiten und diese gleichzeitig vorm Zugriff zu schützen. Dazu kannst dem Attribut noch den Zusatz READ-ONLY vergeben, damit wird das Attribut nur für den lesenden Zugriff freigegeben. Dazu die gänderte Klasse aus dem vorherigen Abschnitt:
CLASS zcl_bs_demo_readonly_data DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
DATA:
mt_messages TYPE string_table READ-ONLY.
METHODS:
add_message
IMPORTING
id_message TYPE string.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_bs_demo_readonly_data IMPLEMENTATION.
METHOD add_message.
INSERT id_message INTO TABLE mt_messages.
ENDMETHOD.
ENDCLASS.
Wenn du nun versuchst schreibend auf das Attribut zuzugreifen, bekommst du bereits zur Compilelaufzeit einen Fehler und du kannst deinen Code nicht mehr aktivieren. Hier mal die Nachricht aus Eclipse:
Das Attribut ist nun gegen Schreibzugriffe geschützt, aber du verlierst damit auch die Vorteile der Getter und Setter Methoden und kannst nicht mehr in den Code eingreifen oder eigene Prüfungen implementieren. Diese Variante empfehlen wir dir nur eingeschränkt.
Fazit
Heute ging es einmal um die Redefinition von Methoden, um so andere Logik in die gleichen Methoden implementieren zu können und so gleiche Klassen mit unterschiedlichem Verhalten zu erstellen. Weiterhin hast du gelernt wie du effizient deine Attribute in einer Klasse verwaltest und dabei die volle Kontrolle über deine Daten behältst.