ABAP OO - Fehlerbehandlung
Wie sieht es mit der Fehlerbehandlung in der objektorientierten Programmierung aus? Auf diese Frage wollen wir in diesem Artikel eingehen.
Inhaltsverzeichnis
Wenn du Software entwickelst, dann kommt es über kurz oder lang zu Ausnahmesituationen, weil Daten fehlen, etwas unvollständig ist oder der Anwender falsche Daten über die UI zur Verfügung gestellt hat. In solchen Fällen sollte sich der Code korrekt verhalten und nicht unkontrolliert abstürzen. Vielleicht möchte der Anwender noch ein Feedback zu seiner Aktion haben oder eine Logging Tabelle muss aktualisiert werden.
Klassisch
Schauen wir uns dazu noch einmal das klassische Fehlerhandling an und welche Nachteile wir aktuell damit haben. Diese Variante kommt noch von den Funktionsbausteinen und wir bei der Methodendefinition auch genau so gehandhabt. Dazu definieren wir eine Methode, die zwei Fehlersituationen beinhaltet:
METHODS:
is_data_valid
IMPORTING
id_data TYPE string
RETURNING VALUE(rd_result) TYPE abap_bool
EXCEPTIONS
empty_data
wrong_key.
Über das Schlüsselwort EXCEPTION definieren wir Fehler, die in der Methode auftreten können und die uns eine Möglichkeit gibt, den Status nach außen zu geben. Entsprechend zur Definition der Methode legen wir auch eine Implementierung an. Hierbei handelt es sich um eine Beispielimplementierung und stellt nur die Funktionsweise in den Vordergrund.
METHOD is_data_valid.
CASE id_data.
WHEN ``.
RAISE empty_data.
WHEN `WRONG`.
RAISE wrong_key.
WHEN OTHERS.
rd_result = abap_true.
ENDCASE.
ENDMETHOD.
Um in einer Fehlersituation den Fehler auslösen zu können und den Code an dieser Stelle zu unterbrechen, verwenden wir das Schlüsselwort RAISE. Die aktuelle Methode wird dann unterbrochen und beendet, es geht weiter am Aufrufpunkt der Methode. Wo der Fehler nun entsprechend über den SUBRC behandelt werden kann.
is_data_valid(
EXPORTING
id_data = `123`
RECEIVING
rd_result = DATA(ld_valid_string)
EXCEPTIONS
empty_data = 1
wrong_key = 2
OTHERS = 3
).
IF sy-subrc <> 0.
" Error handling
ENDIF.
An dieser Stelle ist die Verwendung der klassischen Fehlerbehandlung nicht sehr zielführend und macht die Vorteile der modernen und kurzen Methodenaufrufe wieder zu nichte. Weiterhin gibt es einige Nachteile die zu beachten sind:
- Lange Schreibweise für Methoden
- Transport von Kontextinformationen und Nachrichten schwer
- Unnötige Schlüsselwörter (Exporting, Receiving, Exceptions)
- Abbruch von mehreren Methoden (Verarbeitungskette) schwer
Ausnahmeklasse
Die neue Fehlerbehandlung nutzt für die Weitergabe und die Auslösung von Fehlern sogenannte Ausnahmeklassen, diese unterscheiden sich nur minimal von normalen Klassen und können auch so ähnlich behandelt werden. Dazu orientieren wir uns an dem Beispielcode des klassischen Beispiels und ergänzen entsprechend.
METHODS:
is_data_valid_new
IMPORTING
id_data TYPE string
RETURNING VALUE(rd_result) TYPE abap_bool
RAISING
cx_sy_conversion_data_loss
cx_sy_table_key_specification.
Wie dir aufgefallen ist, verwenden wir anstatt dem Schlüsselwort EXCEPTION nun RAISING, dazu verwenden wir für die beiden Fehler Ausnahmeklassen. Ausnahmeklassen erkennt man am Namen, da sie mit CX_* beginnen. Ansonsten hat sich die Schnittstelle kaum verändert. Sehen wir uns nun die Implementierung der Methode etwas genauer an.
METHOD is_data_valid_new.
CASE id_data.
WHEN ``.
RAISE EXCEPTION TYPE cx_sy_conversion_data_loss.
WHEN `WRONG`.
RAISE EXCEPTION NEW cx_sy_table_key_specification( ).
WHEN OTHERS.
rd_result = abap_true.
ENDCASE.
ENDMETHOD.
Beim Auslösen der Ausnahme haben wir zwei verschiedene Varianten gewählt, einmal die bisherige Variante und im zweiten Fall die neue Variante mit dem Schlüsselwort NEW. In beiden Fällen wird die Verarbeitung abgebrochen und die Ausnahme ausgelöst. Dazu der entsprechende Aufruf der Methode:
DATA(ld_valid_new) = is_data_valid_new( `123` ).
Die Methode ist nun sehr kurz und kann auf eine Zeile geschrieben werden. Dir wird aber auffallen, dass noch keine Fehlerbehandlung implementiert wurde, dies machen wir im nächsten Abschnitt.
TRY/CATCH
Um den Fehler abfangen bzw. darauf reagieren zu können, kannst du nicht mehr auf den SUBRC der Anwendung zurückgreifen. Hier verwendest du ein neues Konstrukt, den TRY/CATCH, um auf den Fehler zu reagieren. So eine Fehlerbehandlung könnte wie folgt aussehen:
TRY.
DATA(ld_valid_new) = is_data_valid_new( `123` ).
CATCH cx_sy_conversion_data_loss INTO DATA(lo_empty).
" Error handling
ENDTRY.
Da die Methode zwei unterschiedliche Fehler erzeugt, sollten wir auch auf beide Varianten reagieren. Hier hast du die Möglichkeit alle Fehler in einem CATCH Zweig abzufangen oder unterschiedlich auf jeden Fehler zu reagieren. Die beiden folgenden Beispiele sollen dir die Schreibweise etwas näher bringen:
" One Catch
TRY.
DATA(ld_valid_new) = is_data_valid_new( `123` ).
CATCH: cx_sy_conversion_data_loss, cx_sy_table_key_specification INTO DATA(lo_error).
" Error handling
ENDTRY.
" Separate Catch
TRY.
DATA(ld_valid_new) = is_data_valid_new( `123` ).
CATCH cx_sy_conversion_data_loss INTO DATA(lo_empty).
" Error handling
CATCH cx_sy_table_key_specification INTO DATA(lo_key).
" Error handling
ENDTRY.
Nach dem TRY wird solange versucht den Code auszuführen, bis der Block beendet ist. Sollte es zu einer Ausnahme kommen, dann wird der folgende Code im TRY Block nicht mehr ausgeführt und direkt in den entsprechenden CATCH verzweigt, hier kannst du deine Fehlerbehandlung oder dein Logging durchführen.
Weitergabe
Durch den Try/Catch Block fängst du einen Fehler ab, musst dies aber nicht mehr zwingend nach dem Aufruf der Methode machen. In der objektorientierten Welt sind Methoden meist nur kleine Codeabschnitte und sauber in verschiedene Aufrufe gekapselt, das hat zur Folge, dass in einer Verarbeitungskette von Methoden ein Fehler meist weit unten in der Hierarchie ausgelöst wird. In solchen Fällen ist der gerade verarbeitete Satz nicht mehr valide und der Abbruch erfolgt weit oben in der Kette.
In solchen Fällen wird die Fehlerbehandlung einfach an der gewünschten Stelle durchgeführt und ein CATCH verarbeitet den Fehler, loggt diesen und bricht den aktuellen Datensatz sauber ab. Ein klarer Vorteil für eine saubere Strukturierung von Software.
Hinweis: Wird eine Ausnahme im gesamten Aufrufstack nicht abgefangen, kommt es zu einer Ausnahme und der Report bricht mit einem Dump ab. Dieses Verhalten kann ebenfalls gewünscht sein, sollte aber geprüft werden, da die Verarbeitung nicht sauber beendet wird.
Zusätze
Zu erwähnen sind noch die Zusätze CLEANUP und RESUME, die innerhalb des Blocks verwendet werden können. Dies erfüllen spezielle Anforderungen beim Abbruch ders Blocks:
- CLEANUP - Hier hast du die Möglichkeiten Nacharbeiten durchzuführen, Datenbankrollbacks durchzuführen oder interene Variablen zu initialisieren.
- RESUME - Wiederaufsetzen der Verarbeitung, wenn eine wiederholbare Ausnahme ausgelöst wurde. Mehr dazu erfährst du in der offiziellen Dokumentation.
Fazit
Heute haben wir dir das Konzept der modernen Fehlerbehandlung über Ausnahmeklassen aufgezeigt und wie du diese effizient verwenden kannst. Im nächsten Artikel werden wir etwas genauer auf die Fehlerklassen eingehen und wie du sie für deine Zwecke optimal gestalten kannst.
Quelle:
SAP Dokumentation - TRY/CATCH
SAP Dokumentation - RESUME