Codequalität ABAP

Posten Sie hier Tutorials & Cookbooks.
12 Beiträge • Seite 1 von 1
12 Beiträge Seite 1 von 1

Codequalität ABAP

Beitrag von jocoder (Specialist / 149 / 2 / 37 ) » 05.12.2017 15:35

Hier habe ich einiger meiner Gedanken zusammengestellt um die Codequalität in ABAP zu steigern.
Diese sind mir im Laufe einer großen Qualitätsverbesserungmaßnahme eingefallen.
Wer Anmerkungen zu diesem Thema hat, kann den Beitrag gerne kommentieren.

Wir beschränken hier uns auf das Thema Design und Dokumentation der Prozeduren (Funktionsbausteine, Methoden).

Was hat eine Änderung für Seiteneffekte?
Wenn wir die Form oder den Inhalt der Prozedurschnittstelle ändern, gibt es 2 Kategorien:

Major-Releases:
Hier funktioniert der bestehende Source-Code, der die Prozedur aufruft, nicht mehr.
Dazu wird z.B. folgendes an einer Prozedur geändert:
  • ein nicht-optionaler Importing- oder Changing-Parameter wird hinzugefügt
  • ein Parameter wird geändert (Typ, Name)
  • ein Parameter wird gelöscht
  • ein optionaler Parameter ist nicht mehr optional
  • eine Ausnahme wird hinzugefügt oder geändert
  • eine Ausnahme wird gelöscht
  • Umstellung von Wert-Übergabe auf Referenz-Übergabe oder umgekehrt eines Exporting- oder Changing-Parameter
Hier muss immer mit dem Verwendungsnachweis aller Source-Code gesucht werden, der die Prozedur aufruft, und entsprechend angepasst werden.
Wird nur die Form der Schnittstelle geändert, kommt es zu einem Syntaxfehler, der relativ einfach erkannt und behoben werden kann.
Wird der Inhalt einer Schnittstelle geändert wird dies nicht unbedingt als Syntaxfehler erkannt.
Trotzdem müssen alle Aufrufer der Prozedur kontrolliert werden.
Bsp:: in der alten Version übergebt eine Prozedur den Bestand als Summe pro Material und Werk im Exporting-Parameter.
Jetzt ändern wir die Prozedur und der Bestand wird als Summe pro Material, Werk und Charge übergeben.

Wir haben jetzt einen Aufrufer, der den Bestand weiterhin auf Material/Werk Ebene liest mit der Anweisung

Code: Alles auswählen.

READ TABLE bestand ... WITH KEY werks = werk matnr = matnr.
Dieser liest jetzt nur noch den Bestand der 1.Charge statt die Summe pro Material und Werk.

Minor-Release
Hier wird nur eine Erweiterung erstellt. Bestehender Source-Code funktioniert weiterhin.
Dazu wird z.B. folgendes an der Prozedur geändert:
  • ein optionaler Importing- oder Changing-Parameter wird hinzugefügt
  • ein Exporting Parameter wird hinzugefügt
  • Umstellung von Wert-Übergabe auf Referenz-Übergabe eines Importing-Parameters
Also egal wie die Prozedur bisher aufgerufen wird, dies soll nach der Änderung unverändert funktionieren.

Einheitliche Schlüsselwörter in den Kommentare (eigene Pragmas)

Hiermit können Kommentare standardisiert werden, um alle wichtigen Informationen zu enthalten.
Diese haben aber keine Funktion in der Syntaxprüfung.

Folgende Kommentare haben sich als sinnvoll herausgestellt:
  • @DEPRECATED: Markiert die Prozedur als obsulet. In Neuentwicklungen sollte dies nicht mehr verwendet werden.
  • @ASSERT_CONDITION: Hinweis auf eine assert-Bedingung. (z.B. Methode cl_gui_alv_grid=>set_ready_for_input Hier wird davon ausgegangen, dass die Methode im Online-Modus gerufen wird und nicht im Druck-Modus).
  • @DYNAMIC_CALLED PGMID= OBJECT= OBJ_NAME= Prozedur wird dynamisch gerufen. PGMID, OBJECT, OBJ_NAME enthält den Objektkatalog-Eintrag der Aufrufer der Prozedur. Dies kann auch eine Customizing-Tabelle sein. (z.B. im Modul PPPI die Prozessmeldungen. Hier werden Funktionsbausteine abhängig von der Customizing-Tabelle tc51 gerufen.
    Die Funktionsbausteine, die die Prozessmeldungen verarbeiten, würde ich mit folgendem Pragme versehen @DYNAMIC_CALLED PGMID=R3TR OBJECT=TABL OBJ_NAME=TC51).
  • @PREPERATION vor Aufruf müssen folgende Vorbereitungen getroffen werden (z.B. Meldungen sammeln starten mit dem Funktionsbaustein MESSAGES_INITIALIZE)
  • @CONSTRUCTOR in einer Funktionengruppe, der Funktionsbaustein, der die selbe Funktion wie ein Constructor einer Klasse übernimmt (z.B. Funktionengruppe SMSG Funktionsbaustein MESSAGES_INITIALIZE). Wie in den Beiträgen erwähnt, sollte dies nicht mit einem Klassenkonstruktor verwechselt werden. Bei Klassen entfällt dieses Schlüsselwort sowieso.
  • @USER_PARAMETER= die Verarbeitung ist abhängig vom einem Parameter im Benutzerstamm (z.B. Buchen eines Materialbeleges mit Funktionsbaustein BAPI_GOODSMVT_CREATE. Hier ist das Drucken eines Warenbegleitscheines abhängig vom Benutzerparameter NDR)
Dynamische Prozeduraufrufe
Diese werden im Verwendungsnachweis nicht gefunden. Dazu können die Aufrufer der Prozedur direkt im Kommentar mit dem Schlüsselwort @DYNAMIC_CALLED erwähnt werden oder im rufenden Source-Code.

Im rufenden Source-Code würde dies wie folgt aussehen:

Code: Alles auswählen.

* call a function module depending on the basis release
data name_fmodule type tfdir-funcname.

CONCATENATE 'ZSAMPLE_READ' sy-saprl INTO name_fmodule.

* we use Release 740, 750
if 1 = 0.
  " called in release 740
  call function 'ZSAMPLE_READ740'.
  " called in release 750
  call function 'ZSAMPLE_READE750'.
endif.
call function name_fmodule.
Somit findet der Verwendungsnachweis die Funktionsbausteine ZSAMPLE_READ740, ZSAMPLE_READ750 wieder.

Dokumentation
Neben der Funktionsbeschreibung sollte bei Strukturen eigentlich immer dokumentiert sein, welche Felder vorher gefüllt werden sollen oder verwendet/verarbeitet werden (Voraussetzung: nur ein Teil der Struktur wird benötigt).

Bei BAPIs oder Funktionsbausteinen, die Fehlermeldungen in einer Tabelle übergeben, kann es von Vorteil sein, wenn bereits alle Fehlermeldungen mit der Ursache in der Schnittstelle dokumentiert sind.
Somit wird die Fehleranalyse deutlich vereinfacht.

Stolperfallen
  • optionale Parameter können leicht vergessen werden. Daher ist es unter Umständen sinnvoller mehrere Prozeduren zu schreiben mit unterschiedlichen Schnittstellen ohne optionale Parameter. Allerdings muss hier auch abgewägt werden, ob die selbe Funktion dadurch mehrmals implementiert wird. Dadurch wächst später der Aufwand den zu Code zu pflegen.
  • Code: Alles auswählen.

    EXPORT ... TO MEMORY ID, IMPORT FROM MEMORY ID.
    Dies sollte nur noch in Ausnahmefällen verwendet werden. Bei größeren Projekten verliert man hier schnell den Überblick. Bei einer späteren Wartung kann es schnell passieren, das eine Memory-ID ungewollt überschrieben wird.
Ausnahmekonzept

Ausnahmen sollten eigentlich dazu dienen, das Programm abzubrechen bevor ein unkontrollierter Zustand entsteht und z.B. ein falscher Beleg gebucht wird.
Diese können in 3 Kategorien unterteilt werden:
  • Ausnahmen durch Programmierfehler (bei einer Wartung/Erweiterung). Sollten immer einen Laufzeitfehler verursachen.
  • Ausnahmen durch falsche Werte in der Datenbank. Hier kann mit einer Fehlermeldung reagiert werden.
  • Ausnahmen durch falsche Eingaben des Benutzers. Kann nur mit einer Meldung reagiert werden.
Mit folgenden Ausnahmen würde ich auf diese Kategorien reagieren
  • Programmierfehler: Klassenbasierte Ausnahme (vom Typ cx_dynamic_check), ASSERT-Bedingung, klassische Ausnahme mit Anweisung RAISE
  • falsche Werte in der Datenbank: Klassenbasierte Ausnahme (vom Typ cx_static_check) oder klassische Ausnahme mit MESSAGE RAISING
  • falsche Benutzereingaben: Klassenbasierte Ausnahme (vom Typ cx_static_check) oder klassische Ausnahme mit MESSAGE RAISING
Die Anweisung MESSAGE wird nur noch in der GUI-Logik verwendet, um GUI und Anwendungslogik klar zu trennen,

In der Schnittstelle sollten alle Ausnahmen propagiert werden, die aufgrund fehlerhafter Schnittstellendaten entstehen.
ASSERT Anweisungen können alternativ verwendet werden, wenn fehlerhafte Schnittstellendaten oder Klassenattribute aufgrund der Wartung/Erweiterung eines Programmes entstehen (z.B. Übergabe eines Zeitraumes, ASSERT_CONDITION Zeitraumende >= Zeitraumanfang). Die ASSERT Anweisungen würde ich dann aber in der Schnittstelle erwähnen.
Zuletzt geändert von jocoder am 09.12.2017 12:00, insgesamt 3-mal geändert.


Re: Codequalität ABAP

Beitrag von black_adept (Top Expert / 3341 / 60 / 623 ) » 05.12.2017 19:15

jocoder hat geschrieben:Stolperfallen

optionale Parameter können leicht vergessen werden. Daher ist es unter Umständen sinnvoller mehrere Prozeduren zu schreiben mit unterschiedlichen Schnittstellen ohne optionale Parameter. Allerdings muss hier auch abgewägt werden, ob die selbe Funktion dadurch mehrmals implementiert wird. Dadurch wächst später der Aufwand den zu Code zu pflegen.
Das Problem mit der Mehrfachimplementierung kannst du leicht umgehen, indem du eine Prozedur mit voller Schnittstelle bereitstellst und die Prozeduren mit reduzierter Schnittstelle diese aufrufen.
@ Pragmas:
  • @DEPRECATED: Gefällt mir, da es durch den CodeInspector überprüft werden kann
  • @ASSERT_CONDITION halte ich für überflüssig - dann kann ich auch gleich ein ASSERT reinschreiben
  • @DYNAMIC_CALLED: Grundsätzlich interessante Idee - aber die Angabe der Aufrufer hier halte ich für die falsche Stelle, da beim Hinzukommen eines weiteren Kosumenten der Prozedur auch die Prozedur mit angefasst werden muss. Lieber im Rufenden Programm so was wie ein IF 1 = 0. PERFORM xxxx IN PROGRAM yyyy. ENDIF einbauen, damit auch der Verwendungsnachweis funktioniert
  • @CONSTRUCTOR: Der Pragmaname ist sehr unglücklich gewählt, da es eher einem Klassenkonstruktor oder einer Initialisierungsmethode entspricht

jocoder hat geschrieben:Dokumentation
...
Bei BAPIs oder Funktionsbausteinen, die Fehlermeldungen in einer Tabelle übergeben, sollte immer dokumentiert sein, mit welchen Fehlermeldungen wir rechnen müssen.
Interessant - aber wenn ich z.B. so was wie einen StandardBAPI von SAP aufrufe habe ich i.A. keine Ahnung was da alles schief gehen könnte. Denn wenn ich das wüsste hätte ich das ja auch vorher abfangen können. Und wenn es mir vorher egal genug ist, dann reicht es mir auch aus wenn die Meldung dann vom BAPI kommt - egal was passiert ist
jocoder hat geschrieben:Die Anweisung MESSAGE wird nur noch in der GUI-Logik verwendet, um GUI und Anwendungslogik klar zu trennen,
Die Anweisung MESSAGE schreibt bei Hintergrundverarbeitung Einträge ins Joblog, was durchaus auch ohne GUI interessant ist.
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Re: Codequalität ABAP

Beitrag von jocoder (Specialist / 149 / 2 / 37 ) » 06.12.2017 08:02

Zum Thema Pragma

@ASSERT_CONDITION: verwende ich in der Dokumentation zu Beginn der Prozedur. Dann fällt diese einem bei Aufruf der Prozedur leicht auf.
Wenn ich eine Methode habe, die noch viele Methoden anderer Klassen aufruft, wird ja eine ASSERT-Condition schnell übersehen.

Re: Codequalität ABAP

Beitrag von jocoder (Specialist / 149 / 2 / 37 ) » 06.12.2017 08:13

Code: Alles auswählen.


class ... definition.

  public section.

  private section.


  "@ASSERT_CONDITION: do not modify after read data in method read_data
  data: example_tab type table of ...
 
  methods read_data.

endclass.

Dafür finde ich das Pragma nützlich. Hier ist dann ersichtlich, dass die Tabelle example_tab nach dem Lesen der Daten
nicht modifiziert werden kann.

Re: Codequalität ABAP

Beitrag von ralf.wenzel (Top Expert / 3474 / 155 / 223 ) » 06.12.2017 09:34

Vielleicht bin ich ja einfach zu doof: Warum als Pragma und nicht als "einfachen" Kommentar? Pragmas sind doch eher was für die Erweiterte Programmprüfung, was willst du da verproben?


Ralf

Re: Codequalität ABAP

Beitrag von jocoder (Specialist / 149 / 2 / 37 ) » 06.12.2017 10:37

Im Prinzip sind dies auch nichts als Kommentare.
Das ist nur unser Standard, um die Kommentare zu verheintlichen.
Meiner Meinung nach sticht dies besser ins Auge und im Code-Inspector kann auch danach gesucht werden.

Letzten Endes kann dies jeder regeln wie er will.

Re: Codequalität ABAP

Beitrag von ralf.wenzel (Top Expert / 3474 / 155 / 223 ) » 06.12.2017 11:06

jocoder hat geschrieben:Im Prinzip sind dies auch nichts als Kommentare.
Eben. Ein PRAGMA ist aber nicht einfach ein Kommentar, sondern eine Steueranweisung an die Erweiterte Programmprüfung. Mit dem Unterschied, dass ihr nix damit steuert. Würde ich sowas finden, würde ich mir als Erstes die Frage stellen "welche Auswirkung hat das Pragma?" - darauf, dass es keine hat, kommt man nicht so leicht.


Ralf

Re: Codequalität ABAP

Beitrag von MrBojangles (Specialist / 362 / 3 / 27 ) » 06.12.2017 14:38

Hallo zusammen,
bei der Durchsicht Eurer Pragmas ist mir als jemand, der mal 3 Wochen Englisch-Aushilfelehrer war, eine winzige Kleinigkeit aufgefallen:
"@PREPERATION" sollte womöglich besser "@PREPARATION" betitelt werden, falls es kein Übertragungsfehler war...
Weiterhin viel Freude mit SAP...
Cheers
MrB.

Re: Codequalität ABAP

Beitrag von ralf.wenzel (Top Expert / 3474 / 155 / 223 ) » 06.12.2017 14:50

Danach darfst du nicht gehen. Ich habe in einem Projekt erlebt, dass man 'POSITION' für Positionseinträge gewählt hat statt 'ITEM'. Da bin ich hinten rübergefahren - sowas kriegt man nie wieder raus.


Ralf

Re: Codequalität ABAP

Beitrag von MrBojangles (Specialist / 362 / 3 / 27 ) » 07.12.2017 08:57

schon klar, Ralf - wenn's bereits etabliert und in Gebrauch ist - bloss nix rühren...
Der Eingangspost klang für mich nur so, als ob das Verfahren noch nicht flächendeckend eingeführt sondern noch in der "Review-Phase" ist. Stach mir nur ins Auge...
Weiterhin viel Freude mit SAP...
Cheers
MrB.

Re: Codequalität ABAP

Beitrag von jocoder (Specialist / 149 / 2 / 37 ) » 07.12.2017 10:20

Wenn wir nach diesem Motto -wenn's etabliert und in Gebrauch ist - bloss nix rühren...
verfahren würden, wären die Qualitätsrichtlinien nie entstanden.

Das haben wir nur geschafft, weil wir mal über den Tellerrand geschaut haben.
Was ich jedem ABAP-Programmierer empflehen würde, wäre ein Blick über den Tellerand was in anderen Projekten so abläuft.
Viele Ideen haben wir aus anderen Projekten übernommen.

Re: Codequalität ABAP

Beitrag von ralf.wenzel (Top Expert / 3474 / 155 / 223 ) » 07.12.2017 10:41

jocoder hat geschrieben:Wenn wir nach diesem Motto -wenn's etabliert und in Gebrauch ist - bloss nix rühren...
verfahren würden, wären die Qualitätsrichtlinien nie entstanden.
Richtig - aber du wirst in einem laufenden Programm kein Klassenattribut umbenennen, das derart zentral ist, dass du damit das ganze Projekt zerschießen kannst. Da muss man sagen "gepennt - Krönchen richten und weitermachen".


Ralf

Seite 1 von 1