ABAP Tipp - CLEAR right
Richtig löschen? In diesem Artikel wollen wir uns einmal anschauen, wann es Sinn macht zu löschen und wie du effektiv vorgehen kannst.
Inhaltsverzeichnis
Wie solltest du heutzutage in der Entwicklung deine Variablen löschen, musst du das überhaupt noch machen und welche Hilfen können wir dir mit auf den Weg gehen. In diesem Artikel wirst du etwas mehr erfahren und zum Löschprofi von uns ausgebildet. Also lehn dich zurück und genieße den Artikel.
Einleitung
Manchmal machen dir Kleinigkeiten bereits das Leben leichter oder eben schwerer, wenn du auf der Suche nach Fehlern bist. Neben dem Löschen von Variablen gibt es auch andere Methoden, um die Daten zu initialisieren und dabei in weniger Probleme zu laufen. Selbst mit dem Design deiner Software kannst du bereits vorher viele Probleme aus dem Weg räumen und die Stabilität erhöhen.
Neben dem direkten Löschen von Variableninhalten, gibt es auch das Überschreiben, den Garbage Collector und das Design deiner Software, die in Betracht gezogen werden können.
CLEAR
Die einfachste Methode zum Aufräumen deiner Variablen ist das einfache Löschen von Variablen, hierbei stehen dir CLEAR, REFRESH und FREE zur Verfügung. Wir empfehlen aber nur noch die Verwendung eines Statements. REFRESH ist obsolet und FREE arbeitet so ähnlich wie CLEAR, weshalb auch vom Namen her CLEAR am besten passt. Dazu einmal ein Beispiel verschiedener Weisen:
" Clear variable (single)
CLEAR ld_field.
" Clear variable (chained statement)
CLEAR: ls_structure, lo_reference.
" Clear table
CLEAR: lt_table.
" Clear table (with header line)
CLEAR: lt_table, lt_table[].
Angefangen vom einfachen Löschen von Variablen, Strukturen und Referenzen, bis hin zum Löschen von Tabellen und Tabellen mit Kopfzeilen.
Hinweis: Die Variante zum Löschen von Tabellen mit Kopfzeilen "[]" kann bei anderen Entwicklern zu Verwirrungen führen, wenn es sich nur um eine einfache Tabelle handelt. Wir empfehlen daher hier mit Bedacht die korrekte Variante zu wählen, auch da Quellcode eingespart werden kann.
Globale Variablen
Variablen die global definiert sind, egal ob in einer Klasse oder einem Report, können zu Fehlern führen, wenn sie bei der Nutzung nicht den richtigen Wert haben oder nicht sauber gelöscht wurden. In der Vergangenheit wurden oft Tabellen und Strukturen, über die geloopt wird, global definiert und sehr wahrscheinlich in Unterroutinen verwendet. Solche Verarbeitungen bergen ein sehr großes Risiko, da man nie weiß, ob die Variablen zum Zeitpunkt der Verarbeitung immer noch korrekt gefüllt sind.
Lokale Variable
Bereits der Umstieg auf lokale Variablen reduziert dieses Problem und sorgt in maximal einer Methode für Probleme. Die Variablen werden nach dem Verlassen der Methode vom Garbage Collector des Systems gelöscht und werden beim nächsten Aufruf der Methode wieder initial gesetzt.
DATA:
lt_table TYPE STANDARD TABLE OF ts_return WITH EMPTY KEY,
ls_structure TYPE ts_return.
" Loop with predefined variable
LOOP AT lt_table INTO ls_structure.
ENDLOOP.
Inline Deklaration
Die Inline Deklaration erzeugt die Variable da wo wir sie brauchen und zeigt an, dass diese Variable zum Beispiel nur für den Loop benötigt wird.
" Loop with inline deklaration
LOOP AT lt_table INTO DATA(ls_inline).
ENDLOOP.
Exporting Parameter
Eine weitere Gefahrenquelle sind Exporting Parameter. Wird der Wert der Exporting Variablen nicht sauber gelöscht, dann kann es je nach Konstellation zu Fehlern kommen. Definieren wir dazu die folgende Methode:
" Definition
METHODS:
method_with_exporting
IMPORTING
id_param1 TYPE i
id_param2 TYPE i
EXPORTING
ed_result1 TYPE td_char
ed_result2 TYPE i
ed_subrc TYPE i.
" Implementation
METHOD method_with_exporting.
ed_result1 &&= 'Test'.
ed_result2 += id_param1 + id_param2.
ed_subrc = 2.
ENDMETHOD.
Führen wir dazu eine Verarbeitung in drei Schritten aus und stellen die Methode in eine DO Schleife, die drei Mal ausgeführt wird. Bei dieser Kombination verwenden wir eine Inline Deklaration und definieren die Variable für den Aufruf der Methode.
DO 3 TIMES.
method_with_exporting(
EXPORTING
id_param1 = 1
id_param2 = 2
IMPORTING
ed_result1 = DATA(ld_result1)
ed_result2 = DATA(ld_result2)
ed_subrc = DATA(ld_subrc)
).
ENDDO.
Prüfen wir nun das Ergebnis der Variablen, würden wir eigentlich ein anderes Ergebnis erwarten. Die Exporting Variablen werden nach dem zweiten Aufruf der Methode nicht initialisiert und der alte Wert führt zu einem fehlerhaften Ergebnis.
Hier solltest du nach dem Aufruf der Methode sofort auch die Exporting Parameter bereinigen, hier könnten noch alte Werte aus einer vorhergehenden Verarbeitung enthalten sein. Das Problem kann vor allem bei Tabellen auftreten, wenn immer wieder Zeilen ans Ende angehangen werden. Ein einfacher CLEAR kann hier schwerwiegende Probleme beheben.
METHOD method_with_exporting.
CLEAR: ed_result1, ed_result2, ed_subrc.
ed_result1 &&= 'Test'.
ed_result2 += id_param1 + id_param2.
ed_subrc = 2.
ENDMETHOD.
Methoden Design
Um das Problem mit den Exporting Parametern zu umgehen, lohnt sich bereits beim Design der Methode auf Exporting Parameter zu verzichten. Laut Clean ABAP, sollte die Methode nur wenige Importing Parameter und einen Returning Parameter besitzen, damit gilt die Methode als Clean. Dieser Umstand sorgt auch dafür, dass wir nicht mehr an den CLEAR bei den Rückgabeparametern denken müssen.
DO 3 TIMES.
DATA(ls_result) = method_with_returning( id_param1 = 1 id_param2 = 2 ).
ENDDO.
Rufen wir nun die Returning Methode drei Mal in Folge auf und verwenden ebenfalls die Inline Deklaration, dann sehen wir mit der gleichen Logik nach jedem Durchlauf das richtige Ergebnis. Wir müssen nun nicht mehr die Variablen vorher löschen und sparen damit weiteren Code, ebenfalls sieht der Aufruf viel schlanker aus.
Konstruktor Ausdrücke
Wie der Name bereits beschreibt, legen solche Ausdrücke immer wieder neue Variablen an und löschen damit den alten Wert automatisch aus der Variable.
Befüllung
Schauen wir uns einmal das folgende Beispiel an:
ls_structure-result1 = 'One'.
ls_structure-result2 = 10.
ls_structure-subrc = 1.
INSERT ls_structure INTO TABLE lt_table.
ls_structure-result1 = 'Two'.
ls_structure-subrc = 2.
INSERT ls_structure INTO TABLE lt_table.
Der zweite Eintrag wird sehr wahrscheinlich einen Fehler haben und das Feld RESULT2 wird den Inhalt des vorherigen Eintrags besitzen. Hier ist ebenfalls nicht klar, ob das Verhalten so gewünscht ist oder es sich um einen Fehler handelt. Mit dem Löschen der Variablen können wir den Fehler vermeiden:
CLEAR ls_structure.
ls_structure-result1 = 'One'.
ls_structure-result2 = 10.
ls_structure-subrc = 1.
INSERT ls_structure INTO TABLE lt_table.
CLEAR ls_structure.
ls_structure-result1 = 'Two'.
ls_structure-subrc = 2.
INSERT ls_structure INTO TABLE lt_table.
Allerdings schreiben wir immer noch recht viel Code und das könnten wir weiter reduzieren, ohne auf den CLEAR achten zu müssen. Dazu verwenden wir direkt einen Konstruktor Ausdruck und sparen uns die Variable, aber auch einiges zum Tippen:
INSERT VALUE #(
result1 = 'One'
result2 = 10
subrc = 1
) INTO TABLE lt_table.
INSERT VALUE #(
result1 = 'Two'
subrc = 2
) INTO TABLE lt_table.
Schleife
In Schleifen arbeiten wir meist mit Mappings und Datenzuweisungen, um die Daten zu verarbeiten. Bevor du mit der Arbeitsstruktur arbeitest, solltest du sie durch einen CLEAR erst einmal initialisieren. Dies kann man am Ende der Schleife machen oder am Anfang. Wir empfehlen das Löschen am Anfang der Schleife, von der Leserichtung beim Suchen nach Fehlern, würde man zuerst prüfen, ob die Struktur sauber initialisiert wurde.
LOOP AT lt_table INTO ls_line.
CLEAR ls_structure.
ls_structure-result1 = ls_line-result1.
ls_structure-result2 = 2.
ENDLOOP.
Auch hier kannst du das CLEAR sparen, wenn du per Konstuktor Ausdruck die Variable erzeugst oder befüllst:
LOOP AT lt_table INTO ls_line.
ls_structure = VALUE #(
result1 = ls_line-result1
result2 = 2
).
ENDLOOP.
Vollständiges Beispiel
Zum Abschluss noch das komplette Beispiel zum selbst Ausprobieren oder Nachstellen im eigenen System:
CLASS zcl_bs_demo_clear_right DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
TYPES:
td_char TYPE c LENGTH 20,
BEGIN OF ts_return,
result1 TYPE td_char,
result2 TYPE i,
subrc TYPE i,
END OF ts_return.
METHODS:
method_with_exporting
IMPORTING
id_param1 TYPE i
id_param2 TYPE i
EXPORTING
ed_result1 TYPE td_char
ed_result2 TYPE i
ed_subrc TYPE i,
method_with_returning
IMPORTING
id_param1 TYPE i
id_param2 TYPE i
RETURNING VALUE(rs_result) TYPE ts_return,
simple_clear,
global_variable,
constructor_expressions.
ENDCLASS.
CLASS zcl_bs_demo_clear_right IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
simple_clear( ).
global_variable( ).
DO 3 TIMES.
method_with_exporting(
EXPORTING
id_param1 = 1
id_param2 = 2
IMPORTING
ed_result1 = DATA(ld_result1)
ed_result2 = DATA(ld_result2)
ed_subrc = DATA(ld_subrc)
).
ENDDO.
DO 3 TIMES.
DATA(ls_result) = method_with_returning( id_param1 = 1 id_param2 = 2 ).
ENDDO.
constructor_expressions( ).
ENDMETHOD.
METHOD simple_clear.
DATA:
ld_field TYPE i,
ls_structure TYPE ts_return,
lt_table TYPE STANDARD TABLE OF ts_return WITH EMPTY KEY,
lo_reference TYPE REF TO zcl_bs_demo_clear_right.
" Clear variable (single)
CLEAR ld_field.
" Clear variable (chained statement)
CLEAR: ls_structure, lo_reference.
" Clear table
CLEAR: lt_table.
" Clear table (with header line)
CLEAR: lt_table, lt_table[].
ENDMETHOD.
METHOD global_variable.
DATA:
lt_table TYPE STANDARD TABLE OF ts_return WITH EMPTY KEY,
ls_structure TYPE ts_return.
" Loop with predefined variable
LOOP AT lt_table INTO ls_structure.
ENDLOOP.
" Loop with inline deklaration
LOOP AT lt_table INTO DATA(ls_inline).
ENDLOOP.
ENDMETHOD.
METHOD method_with_exporting.
CLEAR: ed_result1, ed_result2, ed_subrc.
ed_result1 &&= 'Test'.
ed_result2 += id_param1 + id_param2.
ed_subrc = 2.
ENDMETHOD.
METHOD method_with_returning.
rs_result-result1 &&= 'Test'.
rs_result-result2 += id_param1 + id_param2.
rs_result-subrc = 2.
ENDMETHOD.
METHOD constructor_expressions.
DATA:
lt_table TYPE STANDARD TABLE OF ts_return WITH EMPTY KEY,
ls_line TYPE ts_return,
ls_structure TYPE ts_return.
CLEAR ls_structure.
ls_structure-result1 = 'One'.
ls_structure-result2 = 10.
ls_structure-subrc = 1.
INSERT ls_structure INTO TABLE lt_table.
CLEAR ls_structure.
ls_structure-result1 = 'Two'.
ls_structure-subrc = 2.
INSERT ls_structure INTO TABLE lt_table.
INSERT VALUE #(
result1 = 'One'
result2 = 10
subrc = 1
) INTO TABLE lt_table.
INSERT VALUE #(
result1 = 'Two'
subrc = 2
) INTO TABLE lt_table.
LOOP AT lt_table INTO ls_line.
CLEAR ls_structure.
ls_structure-result1 = ls_line-result1.
ls_structure-result2 = 2.
ENDLOOP.
LOOP AT lt_table INTO ls_line.
ls_structure = VALUE #(
result1 = ls_line-result1
result2 = 2
).
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Fazit
Wie du siehst, ist das Löschen nicht unbedingt schwer, es kommt nur auf die richtige Sichtweise an und wie du für dich mit dem Thema Software-Architektur bzw. Software-Design umgehst. Wir hoffen die Tipps haben dir geholfen, noch einmal das Thema zu durchdringen und du nimmst etwas für deine tägliche Arbeit mit.