This is a test message to test the length of the message box.
Login
|

040: Modern, solid and testable ABAP Code (Part 1)

67

Die digitale Version des betterCode Vortrags zum Thema moderner und testbarer ABAP Code. Dabei schauen wir uns das Thema Software Architektur an und geben Tipps für die Nutzung von ABAP Unit.

Werbung


Dies ist der erste Teil einer mehrteiligen Serie zum Thema moderner, solider und testbarer ABAP-Code. Der Vortrag wurde auf dem betterCode Event in Deutsch gehalten und noch einmal digital in englischer Version zur Verfügung gestellt.

 

Agenda

Dabei werden wir zuerst eine kurze Einleitung geben und klären, was dich in diesem Vortrag erwartet. Anschließend über das Thema Muster und verschiedene Design Patterns sprechen, die wir in der modernen Entwicklung verwenden. Uns danach etwas genauer mit ABAP Unit sowie den verschiedenen Mustern und Möglichkeiten beschäftigen. Und am Ende noch einmal auf verschiedene Tipps eingehen, die bei der Automatisierung der Systemlandschaft helfen.

 

Einleitung

In der Einleitung wollen wir die Frage klären: Wieso sollten wir diesem Thema überhaupt noch nachgehen? Dabei stellt sich die grundlegende Frage: Warum schreiben wir heute überhaupt noch ABAP-Code? Der moderne ABAP-Stack besteht mittlerweile aus SAP Fiori Elements für die Benutzeroberflächen, der Datenmodellierung über Core Data Services (CDS), und KI-Unterstützung, die uns hilft, die verschiedenen Bestandteile zu generieren. Wieso also noch selbst codieren? Ist ABAP nicht eigentlich schon obsolet?

Schauen wir uns einmal den RAP-Stack im Detail an. Dabei sehen wir auf der linken Seite das gesamte Datenmodell. Dieses besteht grundsätzlich aus der Tabellenquelle, dem Core Data Services, weiteren Inhalten für Werthilfen und dem eigentlichen Service. Auf der Ebene des Views haben wir die eigentliche Fiori-Applikation, die für die Visualisierung zuständig ist. Auf der rechten Seite findest du das Verhalten und die Verhaltensimplementierung, welche wiederum die Controller-Logik enthält. Dabei übertragen wir das Datenmodell in das MVC-Konzept (Model View Controller). Das stimmt zwar nicht zu 100 %, bietet aber einen guten Ansatz, um die verschiedenen Bestandteile des Modells zu klassifizieren. Deshalb ist auch der Controller immer noch sehr wichtig. Da wir hier ABAP-Code verwenden, ist ABAP Unit für die Testbarkeit und Lesbarkeit essenziell. Wir sollten konsequent Clean ABAP verwenden, dazu kommen wir später noch einmal im Detail. Zudem sollten wir eine klare Struktur und Kapselung einhalten, damit der ABAP-Code nicht "überläuft". Nichts ist schlimmer als eine Action-Implementierung, die über 500 Zeilen Code in einer einzigen Methode enthält. Dafür benötigen wir eine saubere Kapselung und entsprechende Objekte, die diese Logik sinnvoll aufteilen.

Was du von diesem Vortrag eigentlich erwarten kannst? Nun, was du nicht erwarten kannst, ist eine Basisschulung in ABAP OO oder ABAP Unit. Dafür gibt es genügend Grundlagenschulungen, die wesentlich tiefer in die Details der einfachen Basics gehen. Dafür kannst du jedoch erwarten, dass du erweiterte Techniken an die Hand bekommst, wie verschiedene Muster und Design Pattern, die wir verwenden, um testbaren Code aufzubauen. Weiterhin erhältst du eine Einleitung in verschiedene Ansätze, wie du testbare ABAP-Landschaften erstellst, diese wartest und regelmäßig automatisierte Tests ausführst.

 

Pattern

Versuchen wir deshalb, erst einmal in die verschiedenen Muster einzusteigen. Diese sind nicht alle klassische Design Pattern, sondern manchmal einfach eine gewisse Art und Weise, wie man Objekte im ABAP-System verwendet. Dabei folgen wir immer der gleichen Grundstruktur: Wir haben eine Klasse, die einen Zeitstempel generiert und diesen speichert. Der Zeitstempel wird nach dem Speichern als öffentliches Attribut zur Verfügung gestellt, damit er direkt genutzt werden kann, ohne eine entsprechende Getter- oder Setter-Methode verwenden zu müssen. Wir gehen dabei eine Kette von aufeinander aufbauenden Mustern entlang. Von der statischen Klasse, über die Instanzklasse bis hin zur Interface-Definition, der Factory-Methode und der eigentlichen Factory. Diese Pattern bauen so aufeinander auf, dass wir später einen Gesamteindruck davon erhalten, was sich durch die jeweilige Verwendung konkret verbessert hat. Am Ende werden wir uns noch den Singleton anschauen, eine besondere Art, ein Objekt zu definieren und zu verwenden.

 

Statisch

Die statische Klasse ist dabei das einfachste Pattern, das du verwenden kannst. Du hast eine Klasse angelegt, in der alle Methoden und Attribute statisch definiert sind. Dies entspricht im Grunde einer Funktionsgruppe. Es ist nicht wirklich ein gutes Design, da wir hier keine echte Wiederverwendbarkeit im Sinne der Objektorientierung (wie Vererbung oder Polymorphie) haben. Grundsätzlich haben wir die Logik damit aber erst einmal sauber in einem Objekt gekapselt. Das eigentliche Attribut, der Timestamp, ist auf READ-ONLY gesetzt. So stellen wir sicher, dass wir ihn von außerhalb zwar lesen, aber nicht direkt manipulieren können. Das Ändern des Wertes überlassen wir exklusiv der Methode SAVE_TIMESTAMP. Die Methode GET_TIMESTAMP ermittelt einen aktuellen Zeitstempel und gibt diesen zurück. Für die Verwendung haben wir eine eigene Klasse angelegt, diese findest du auch im Demo-Material. Hier rufen wir die Methode SAVE_TIMESTAMP auf, die sich einen aktuellen Zeitstempel holt und diesen in der Klasse speichert. Über das öffentliche Attribut können wir den Zeitstempel auslesen und am Ende ausgeben. Der Haken an der Sache: Die Klasse ist aktuell nicht wirklich flexibel. Da sie statisch ist, können wir keine unabhängigen Instanzen erzeugen, sondern arbeiten immer auf demselben globalen Zustand. Wir können also zur selben Zeit genau einen Zeitstempel speichern und verarbeiten.

 

Instanz

Als Nächstes schauen wir uns die Instanz an. Dabei ist der einzige Unterschied zur vorhergehenden Variante, dass alle Definitionen, die mit CLASS-DATA oder CLASS-METHODS begonnen haben, nun durch einfache DATA- und METHODS-Anweisungen ersetzt wurden. Ansonsten müssen wir keine Änderungen vornehmen, haben damit aber schon weitreichendere Möglichkeiten. Schauen wir uns im nächsten Schritt die Nutzung der Klasse an: Wir können nun zum Beispiel eine Tabelle vom Typ der Klasse erzeugen, in der wir verschiedene Instanzen speichern. In unserem Beispiel machen wir das Ganze dreimal. Wir erzeugen jeweils eine neue Instanz mit dem NEW-Operator, rufen die Logik auf und speichern den jeweiligen Timestamp. Diese Instanz fügen wir dann unserer Tabelle hinzu. Wir erhalten eine Tabelle mit drei Instanzen, in denen jeweils unterschiedliche Zeitstempel gespeichert wurden. Damit haben wir den globalen Zustand der statischen Klasse überwunden.

 

Interface

Als ersten Schritt legen wir ein neues Interface an. Ein Interface dient als „Bauplan“ für eine Klasse. In diesem Fall können wir alle Bestandteile der Klasse, die sich zuvor in der Public Section befanden, einfach übernehmen und müssen keine weiteren Änderungen am Definitionsteil vornehmen. Dieses Interface implementieren wir nun in einer neuen Klasse. Die entsprechenden Definitionen finden wir anschließend in der Public Section wieder. Die Namen der Methoden ändern sich dabei geringfügig, da nun der Interface-Name Bestandteil des Methodennamens ist. Für den Zugriff müssen wir jeweils über das Interface gehen, wenn wir eine öffentliche Methode des Interfaces aufrufen wollen. Für den Zugriff verwenden wir in diesem Fall einen Alias. Dieser ermöglicht uns eine kürzere Schreibweise eines Attributes aus dem Interface. Grundsätzlich würden wir diese Vorgehensweise jedoch nicht uneingeschränkt empfehlen, da dadurch die Suche nach dem Originalelement innerhalb des Codes erschwert wird. Im nächsten Schritt erstellen wir eine neue Klasse mit einer zunächst leeren Implementierung. Hierfür binden wir das Interface in die Klasse ein. Die Methoden bleiben in diesem Stadium noch ohne Logik. Diese Vorgehensweise ermöglicht es uns, über ein einziges Interface mehrere verschiedene Implementierungen anzulegen. Diese besitzen nach außen hin alle dieselbe Schnittstelle, weisen jedoch im Detail ein unterschiedliches Verhalten auf.

Wie wird das Ganze nun eigentlich genutzt? Dazu verwenden wir eine Instanz vom Typ des Interfaces sowie eine Tabelle, die verschiedene Objekte aufnimmt, welche dasselbe Interface implementieren. Zunächst instanziieren wir die Klasse mit dem Interface, für die wir eine konkrete Implementierung vorliegen haben, und hängen dieses Objekt an das Ende unserer Instanztabelle an. In einem weiteren Schritt können wir genauso für die leere Implementierung eine Instanz erzeugen. Dabei können wir dieselbe Variable verwenden, um die Instanzen nacheinander zu erzeugen und sie jeweils in die Instanztabelle zu übernehmen. Eine kleine Herausforderung stellt jedoch der Umgang mit den verschiedenen Instanzen und den Interfaces dar. Da das Interface die öffentliche Schnittstelle definiert, dient es primär der Arbeit mit den verschiedenen Objekten, da dort alle relevanten Methoden und Attribute festgelegt sind. Haben wir jedoch eine Instanz vom Typ der Klasse vorliegen, müssen wir, zum Beispiel für den Aufruf einer Methode wie save_timestamp, explizit über das Interface gehen. Dies macht den Aufruf im Code relativ lang. Als Alternative könnten wir einen Cast durchführen. Dazu definieren wir eine Referenzvariable vom Typ des Interfaces und weisen ihr das Klassenobjekt mittels Casting-Operator zu. Das verkürzt zwar den eigentlichen Methodenaufruf, erfordert jedoch den zusätzlichen Schritt des Castings. Die Arbeit mit Interfaces ist daher oft ein Abwägen, da wir entweder den langen Pfad über die Interface-Komponenten wählen oder durch einen Cast die entsprechende Zielreferenz sicherstellen müssen.

 

Ausblick

In dieser Folge haben wir gesehen, dass ABAP  und Unit Tests immer noch wichtige Bestandteile der Entwicklung sind, auch wenn wir mittlerweile verstärkt auf RAP-Objekten aufbauen. Dabei benötigen wir gewisse Grundkenntnisse der Objektorientierung, um saubere und wartbare Objekte zu erstellen. Wir haben heute damit begonnen, drei der typischen Grundinstanzen von Objekten zu ergründen. In der nächsten Folge werden wir uns dann die erweiterten Design Pattern anschauen und prüfen, wie diese sinnvoll eingesetzt werden können. Vielen Dank fürs Zuschauen und bis zum nächsten Mal!

 

Weitere Referenzen:
YouTube - Part 1
GitHub - Beispiele


Enthaltene Themen:
YouTubeSkriptModernABAPClean ABAP
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Dienstag und Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


043: Modern, solid and testable ABAP Code (Part 4)

Kategorie - YouTube

Die digitale Version des betterCode Vortrags zum Thema moderner und testbarer ABAP Code. Dabei schauen wir uns das Thema Software Architektur an und geben Tipps für die Nutzung von ABAP Unit.

25.05.2026

042: Modern, solid and testable ABAP Code (Part 3)

Kategorie - YouTube

Die digitale Version des betterCode Vortrags zum Thema moderner und testbarer ABAP Code. Dabei schauen wir uns das Thema Software Architektur an und geben Tipps für die Nutzung von ABAP Unit.

18.05.2026

041: Modern, solid and testable ABAP Code (Part 2)

Kategorie - YouTube

Die digitale Version des betterCode Vortrags zum Thema moderner und testbarer ABAP Code. Dabei schauen wir uns das Thema Software Architektur an und geben Tipps für die Nutzung von ABAP Unit.

11.05.2026

038: Recycling-Heroes - Annotations (Document)

Kategorie - YouTube

In dieser Folge schauen wir uns die Annotationen der Dokumenten App an und wie wir diese sehr einfach anlegen können. Dazu erweitern wir die App und fixen ein Problem mit dem Schlüssel.

30.03.2026

037: Core Data Service [Basics] - View and View Entity

Kategorie - YouTube

Schauen wir uns einmal die klassische View im Unterschied zur modernen View Entity an. Dabei gehen wir auf kleinere Unterschiede und die Migration in ABAP ein und wie du Core Data Services einfacher handhaben kannst.

16.03.2026