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

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

56

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


In der zweiten Folge werden wir mit dem Vortrag weitermachen und uns die restlichen Pattern anschauen, die wir beim letzten Mal besprochen, aber nicht zu Ende geführt haben. Daher starten wir direkt mit den nächsten Mustern.

 

Factory Method

Die Factory-Methode soll das eigentliche Problem beheben. Dabei erstellen wir eine statische CREATE-Methode, die uns als Ergebnis ein Objekt vom Typ des Interfaces zurückgibt. Dadurch können wir dafür sorgen, dass bereits der richtige Typ zurückgegeben wird und wir kein Casting mehr durchführen müssen. In der Klasse setzen wir die Instanziierung auf PRIVATE, sodass die Klasse nur noch über die Methode CREATE erzeugt werden kann und nicht mehr direkt über den Constructor. Weiterhin haben wir die Möglichkeit, in der CREATE-Methode Vorarbeiten zu leisten, weitere Methoden nach der Erzeugung der Instanz aufzurufen und zum Beispiel eine Initialisierung durchzuführen. Die Methode heißt deshalb CREATE, weil sie eine neue Instanz des Objekts erzeugt und an den Aufrufer zurückgibt. Weitere Anpassungen sind hier nicht mehr nötig.

Bei der Nutzung der Factory-Methode müssen wir nur noch die Instanz binden und können direkt danach zum Beispiel die Method Chaining-Funktionalität nutzen. In einem zweiten Beispiel rufen wir nach der Instanzerzeugung direkt die Get-Timestamp-Methode auf. Wir verketten somit den Methodenaufruf, um direkt einen neuen Zeitstempel zu erhalten, ohne die Instanz vorher lokal in einer Variable speichern zu müssen. Diese Verkettung macht es relativ einfach und überschaubar, dieses Objekt zu verwenden.

 

Factory

Die Factory soll das eigentliche Problem der Factory-Methode beheben. Denn bei der herkömmlichen Factory-Methode haben wir oft das Problem, dass wir immer noch an die eigentliche Implementierung der Klasse gebunden sind. Damit ist das Mocking der Instanz relativ schwierig und ein Austausch der eigentlichen Implementierung nahezu unmöglich. Um dies zu lösen, legen wir die eigentliche Implementierung in einer privaten globalen Klasse an. Diese Klasse ist nicht dafür gedacht, direkt von außen genutzt zu werden, sondern wird lediglich über das Interface angesprochen. Die Instanziierung der Klasse ist auf PRIVATE gesetzt. Da wir in der Klasse selbst jedoch keine CREATE-Methode zur Verfügung stellen, um das Objekt zu erzeugen, arbeiten wir an dieser Stelle mit einem Global Friend. Über diesen Friend-Bezug berechtigen wir die eigentliche Factory-Klasse, das Objekt im System zu erzeugen. An der restlichen Implementierung ändert sich dadurch nichts. Die eigentliche Factory übernimmt nun die CREATE-Methode, wie wir sie bereits von der Factory Methode kennen. Dabei erzeugt die Klasse eine Instanz unserer eigentlichen Implementierung und gibt sie als Typ des Interfaces zurück. Die Factory-Klasse selbst setzen wir auf ABSTRACT, damit von ihr keine Instanz erzeugt werden kann. Ebenso stellen wir die Klasse auf CREATE PRIVATE ein. Damit ist sichergestellt, dass lediglich die statische CREATE-Methode aufgerufen werden kann, um ein neues Objekt anzulegen. Ein direkter Zugriff oder eine Instanziierung der Factory ist somit ausgeschlossen, da dies auch nicht benötigt wird.

Die Verwendung und der Aufruf sind dann sehr ähnlich zur eigentlichen Factory-Methode, denn wir können hier ebenfalls eine Methodenverkettung durchführen oder uns eine neue Instanz zurückgeben lassen. Der einzige Unterschied besteht darin, dass wir nicht direkt mit der Implementierung arbeiten, sondern immer die Factory nutzen, um ein Objekt zu erhalten. Dadurch entkoppeln wir den Aufrufer von der konkreten Klasse bzw. Implementierung.

 

Singleton

Schauen wir uns als letztes Muster das Singleton einmal im Detail an. Dabei ist das Singleton, wie der Name schon sagt, dafür zuständig, dass wir immer genau eine Instanz zurückgeben und somit stets dieselbe Instanz vorliegen haben. Das hilft uns später bei der Verarbeitung. Wir müssen die Instanz zur Laufzeit nicht selbst in einer lokalen Variable halten, sondern rufen einfach immer wieder die Methode auf, um die exakt gleiche Instanz zu erhalten. Voraussetzung hierfür ist allerdings, dass wir uns in der gleichen Session befinden. Der Unterschied zur Factory-Methode ist, dass wir hier keine CREATE-Methode haben, sondern eine Methode namens GET_INSTANCE. Diese liefert uns eine Instanz zurück, entweder zu einem Interface oder, wie in diesem Fall, zur eigentlichen Klasse, da wir hier nur eine Implementierung haben. Zusätzlich gibt es ein statisches Attribut, welches das Singleton hält. Die Methode GET_INSTANCE prüft zuerst, ob das Singleton bereits erzeugt wurde. Ist dies noch nicht der Fall, erzeugen wir eine neue Instanz, ansonsten geben wir die bereits existierende Instanz zurück. So ist sichergestellt, dass wir systemweit über die gleiche Instanz arbeiten.

In dem gezeigten Beispiel lassen wir uns eine Instanz geben und speichern darin einen Zeitstempel. Danach lassen wir uns über die GET_INSTANCE-Methode eine neue Instanz erzeugen, wobei es sich technisch gesehen natürlich immer noch um die ursprüngliche Instanz handelt. Gehen wir nun fälschlicherweise davon aus, dass wir eine leere Instanz vor uns haben. Wenn wir jetzt jedoch die beiden Instanzen und deren Zeitstempel miteinander vergleichen, werden wir feststellen, dass es sich um exakt denselben Zeitstempel handelt, obwohl wir beim zweiten Aufruf keine Speichermethode ausgeführt haben. Das liegt daran, dass wir über das Singleton-Pattern immer die identische Instanz zurückerhalten. Das bedeutet. Die Änderung, die wir im ersten Schritt vorgenommen haben, ist auch im zweiten Aufruf bereits vorhanden. Damit sind die Zeitstempel identisch.

 

Beispiele

Um die Pattern besser verstehen zu können, schauen wir uns diese einmal direkt im Standard an. Dazu haben wir einige Beispiele herausgesucht, die so bereits im SAP-Standard existieren. Wir referenzieren hierbei auf ein ABAP Environment, welche in der Cloud läuft und entsprechende APIs zur Verfügung stellt. Diese verschiedenen APIs werden wir uns nun im Detail anschauen, die unterschiedlichen Patterns daran definieren und uns die jeweilige Implementierung genau ansehen. Sehr wahrscheinlich werden wir auch auf Abweichungen stoßen, die nicht em gezeigten Standard oder der Verwendung entsprechen.

Schauen wir uns dazu zuerst einmal eine statische Klasse an. Die Klasse CL_ABAP_CONTEXT_INFO ist eine wichtige Klasse, da sie uns in ABAP Cloud mit Informationen aus den Systemfeldern versorgt, wie zum Beispiel den Userdaten, Systemdaten oder Zeiten. Schauen wir uns die Klasse einmal in der Outline-Ansicht an, fällt auf, dass es sehr viele Typen gibt, die für die Nutzung nach außen deklariert sind, da es sich um eine API handelt. In diesem Zusammenhang ist zudem jede Methode als statisch definiert, weshalb eine Instanziierung nicht notwendig ist. Ebenfalls auffällig ist, dass es keinen Konstruktor gibt und somit auch keine Instanziierung vorgesehen ist. Grundsätzlich ist die Klasse noch auf CREATE PUBLIC gesetzt, das heißt, Instanzen könnten theoretisch angelegt werden.

Die zweite Klasse ist eine instanzbasierte Klasse für das Zippen von Binärdateien. In der Outline finden wir den Konstruktor, damit ist sie bereits eine sehr gute Repräsentation für eine Instanzklasse und zeigt, dass sie auch als Instanz verwendet werden soll. Was wir nicht sehen, ist ein Interface. Damit ist keine einheitliche Schnittstelle oder ein einheitlicher Bauplan für die Erstellung definiert worden. Bestimmte Attribute, wie zum Beispiel Konstanten, die öffentlich verfügbar sind, sind statisch, das liegt jedoch an der Natur des Typs der Konstante. Weiterhin gibt es viele private Methoden, die für die Auslagerung des Codings und die interne Verwaltung gedacht sind.

Bei der dritten Klasse handelt es sich um eine Implementierung für einen Application Job. Das sehen wir vor allem daran, dass ein Interface verwendet wird, um die einzelnen Methoden zu implementieren. Schauen wir uns dann allerdings die konkreten Implementierungen in den Methoden an, sehen wir, dass hier nur rudimentäre Informationen vorhanden sind. Da wir uns in einer Interface-Implementierung befinden, müssen wir in der Klasse jede Methode des Interfaces auch wirklich ausprägen. Das sehen wir vor allem in der letzten Methode, die keinen eigentlichen ausführbaren Code enthält, aber mit einem Pragma versehen wurde, um zu kennzeichnen, dass diese Methode benötigt wird. Da die Klasse den Zusatz "Base" im Namen trägt, gehen wir davon aus, dass es sich um eine Basisimplementierung handelt, die immer dann gezogen wird, wenn keine spezifischere Implementierung vorhanden ist, weshalb sie hier leer implementiert wurde.

Bei der nächsten Klasse handelt es sich um eine Factory-Methode. Das sehen wir im ersten Schritt daran, dass wir eine statische CREATE-Methode haben, die exakt die gleiche Signatur besitzt wie der Konstruktor. Die Erstellung der Instanzen wurde auf PRIVATE gesetzt, sodass keine Instanzen direkt über den NEW-Operator erzeugt werden können. In der Implementierung der CREATE-Methode sehen wir, dass wir ein neues Objekt anlegen und die Parameter eins zu eins durchreichen. Dieses Objekt geben wir anschließend über den RETURNING-Parameter zurück. Damit wird eine Instanz der Klasse erzeugt, also der typische Anwendungsfall für eine Factory-Methode.

Im letzten Beispiel schauen wir uns eine Factory an. Dazu sehen wir, dass sie erstens den Begriff "Factory" bereits im Namen hat. Dann besitzt sie wieder eine statische Methode, welche die Instanzen erzeugt, und ein entsprechendes Interface, welches die Implementierung vornimmt. Die Create-Instance-Methode legt Instanzen der Klasse an, in diesem Fall der aktuellen Klasse selbst. Das heißt, wenn wir uns das Objekt genauer anschauen, sehen wir hier eine gemischte Implementierung aus einer Factory Method und einer eigentlichen Factory. Die Erzeugung ist auch hier auf PRIVATE gesetzt. Da die einzelnen Methoden im unteren Teil direkt implementiert sind, können wir eher davon ausgehen, dass es sich hier faktisch um eine Factory Method handelt und nicht, wie im Namen der Klasse angegeben, um eine Factory. Daher ist es besonders wichtig, die Objekte korrekt zu benennen und sie entsprechend ihrem Use Case einzusetzen.

Zum Abschluss schauen wir uns noch das Singleton an. In diesem Fall haben wir eine Klasse, die eine Methode GET_CONTAINER besitzt. Diese wiederum verwaltet das entsprechende Singleton. In den privaten Attributen siehst du eine Referenz vom Typ des Containers, das ist unsere Singleton-Instanz. In der GET_CONTAINER-Methode prüfen wir immer zuerst, ob das Singleton bereits erzeugt wurde. Falls nicht, erzeugen wir über eine Factory ein entsprechendes Objekt, speichern es im Attribut und geben fortan immer nur diese eine Instanz zurück. Weiterhin hat die Klasse noch eine Initialize-Methode, mit der wir das Singleton zurücksetzen können, um zum Beispiel eine neue Verarbeitung zu starten. Zwei kritische Punkte fallen hier jedoch auf: Erstens kann man fälschlicherweise noch eine Instanz der Klasse erzeugen, was bei einem Singleton keinen Sinn ergibt. Zweitens referenziert die Methode GET_CONTAINER im Namen nicht darauf, dass wir ein Singleton zurückerhalten, sondern nutzt nur den eigentlichen Klassennamen.

 

Ausblick

Damit haben wir uns die verschiedenen objektorientierten Patterns im Detail angeschaut und gesehen, wie sie aufeinander aufbauen und entsprechende Weiterentwicklungen des eigentlichen Typs darstellen. Dabei war es uns besonders wichtig, den SAP-Standard zu betrachten, um zu zeigen, dass genau dort diese Muster konsistent verwendet werden. Wir können an den Objekten direkt ablesen, was der eigentliche Zweck des Objekts ist und wie dieses im Kontext von RAP, Fiori Elements oder den Core Data Services genutzt werden soll. In der nächsten Folge werden wir in das Thema ABAP Unit einsteigen und uns die Patterns im Detail anschauen sowie die Vorteile, die sie beim Testen bieten. Dabei werden wir auf die Besonderheiten eingehen und uns anschließend das automatisierte Testen über verschiedene Werkzeuge im Standard ansehen. Bis dahin ... danke fürs Zuschauen und bis zum nächsten Mal.

 

Weitere Referenzen:
YouTube - Part 2
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

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

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.

04.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