ABAP - Schlüsselwortdokumentation →  ABAP - Programmierrichtlinien →  ABAP-spezifische Grundregeln → 

ABAP Objects als Programmiermodell

Hintergrund

ABAP ist eine hybride Programmiersprache, die sowohl ein prozedurales als auch ein objektorientiertes Programmiermodell unterstützt. Das prozedurale Programmiermodell beruht auf der Modularisierung von Programmen in die klassischen Verarbeitungsblöcke, das heißt Ereignisblöcke, Dialogmodule, Funktionsbausteine und Unterprogramme. In ABAP Objects tritt die Klasse konzeptionell an die Stelle des klassischen Programms, und die Modularisierung erfolgt durch deren Methoden. (Technisch gesehen, sind Klassen nach wie vor in Programmen deklariert und implementiert.)

Beide Modelle sind in der Weise interoperabel, dass in klassischen Verarbeitungsblöcken auf Klassen zugegriffen werden kann und innerhalb von Methoden wiederum klassische Programme und Prozeduren aufgerufen werden können. Der hybride Charakter der Sprache ist in erster Linie der Abwärtskompatibilität geschuldet, da ABAP prozedurale Wurzeln hat und sowohl ganze Programme als auch wiederverwendbare Prozeduren (in erster Linie Funktionsbausteine) mit Einführung des objektorientierten Programmiermodells weiterhin nutzbar bleiben sollten.

Regel

ABAP Objects verwenden

Verwenden Sie bei der Neu- und Weiterentwicklung so weit wie möglich ABAP Objects. Klassische Verarbeitungsblöcke dürfen nur noch in Ausnahmefällen neu angelegt werden.

Details

Die Forderung nach der Trennung der Belange wird am besten durch eine weitestgehende Verwendung von ABAP Objects unterstützt. Dass die objektorientierte Programmierung - und hier insbesondere ABAP Objects im Vergleich zum klassischen prozeduralen ABAP hat folgende Gründe:

  1. Datenkapselung
    ABAP Objects ermöglicht eine fortgeschrittene Art der Datenkapselung. Bei der klassischen prozeduralen Programmierung wird der Zustand einer Anwendung durch den Inhalt von globalen Variablen bestimmt. In der objektorientierten Programmierung ist der Zustand in Klassen oder Objekten als Instanzen von Klassen gekapselt. Die Aufteilung der Daten in die verschiedenen Sichtbarkeitsbereiche einer Klasse - öffentlich, geschützt und privat - sorgt für eine klare Unterscheidung zwischen extern und intern verwendbaren Daten. Selbst ohne eine tief gehende objektorientierte Modellierung profitieren Anwendungsprogramme hinsichtlich Robustheit und Wartbarkeit von diesen Eigenschaften.
  2. Explizite Instanzierung
    ABAP Objects ermöglicht die mehrfache Instanzierung einer Klasse über explizite Objekterzeugung mittels der Anweisung CREATE OBJECT. Jede Instanz einer Klasse (Objekt) hat einen eigenen Zustand, der durch die Werte ihrer Attribute festgelegt wird und über die Methoden der Klassen geändert werden kann. Eine automatische Garbage Collection sorgt dafür, dass Objekte, die nicht mehr benötigt werden, aus dem Speicher gelöscht werden. Im prozeduralen Modell gibt es keine Mehrfachinstanzierung, weshalb dort mit zustandslosen Funktionen auf getrennt abgelegten Daten gearbeitet werden muss.
  3. Vererbung
    ABAP Objects ermöglicht die Wiederverwendung von Klassen durch Vererbung, wobei Klassen mit speziellen Verhaltensweisen von allgemeineren Klassen abgeleitet werden und nur die Unterschiede neu implementiert werden müssen. Im prozeduralen Modell können vorhandene Funktionen nur genauso verwendet werden, wie sie sind, oder es müssen neue angelegt werden.
  4. Interfaces
    In ABAP Objects können Objekte über eigenständige Interfaces angesprochen werden. Dies befreit Entwickler davon, sich um Implementierungsdetails der hinter dem Interface liegenden Klasse kümmern zu müssen. Dadurch kann der Anbieter eines Interface die dahinterliegenden Implementierungen ändern, ohne dass die Programme, die das Interface verwenden, modifiziert werden müssen. Im prozeduralen Modell gibt es kein solches Konzept eigenständiger Interfaces.
  5. Ereignisse
    ABAP Objects erleichtert die Implementierung ereignisgetriebener Programmabläufe. Anwendungen können über einen Publish-and-Subscribe-Mechanismus lose gekoppelt werden, wobei der Auslöser eines Ereignisses nichts über eventuelle Behandler wissen muss. Dies erlaubt größere Flexibilität im Vergleich zum prozeduralen Ansatz, bei dem Programme stärker gekoppelt sind und der Programmablauf in der Regel viel starrer vorgegeben ist.
  6. Explizite orthogonale Konzepte
    In ABAP Objects gibt es eine kleine Anzahl genau definierter fundamentaler und zueinander orthogonaler Konzepte, die es zuverlässiger und weniger fehleranfällig als das klassische ABAP machen. Im klassischen prozeduralen ABAP dominieren implizite Verhaltensweisen, in denen Programme durch implizite Ereignisse der Laufzeitumgebung und über globale Daten gesteuert werden. Die Konzepte von ABAP Objects werden dagegen in einem Programm explizit wiedergegeben. ABAP Objects ist damit im Vergleich zum klassischen prozeduralen ABAP einfacher erlern- und anwendbar.
  7. Bereinigte Syntax
    In ABAP Objects gelten bereinigte Syntax- und Semantikregeln. Das klassische prozedurale ABAP ist eine evolutionär gewachsene Sprache mit vielen obsoleten und sich überschneidenden Konzepten. Mit Einführung von ABAP Objects bot sich mit Klassen und Methoden ein Feld für bereinigte Syntax- und Semantikregeln, das von Anforderungen an die Abwärtskompatibilität völlig unbelastet war. Auf diese Weise konnten in ABAP Objects, das heißt innerhalb von Klassen und Methoden, die meisten obsoleten und fehleranfälligen Sprachkonstrukte syntaktisch verboten werden. Zusätzlich werden fragwürdige und potenziell fehlerhafte Zugriffe auf Daten schärfer überprüft und gegebenenfalls ebenso verboten. Die Syntaxbereinigung erzwingt in Klassen eine Verwendung der Sprache ABAP, wie sie außerhalb von Klassen nur durch die Richtlinie zu modernem ABAP gefordert werden kann.
  8. Zugang zu neuen Technologien
    ABAP Objects ist oft der einzige Weg, um mit neuen ABAP-Technologien umzugehen. Beispielsweise bieten GUI Controls, Web Dynpro ABAP, Run Time Type Services (RTTS) oder das Internet Connection Framework (ICF) ausschließlich klassenbasierte Schnittstellen an. Wenn Programme, die solche Services verwenden, weiterhin rein prozedural implementiert würden, käme es zu einer unnötigen Vermischung der Programmiermodelle mit entsprechender Erhöhung der Komplexität.

Die dringende Empfehlung zur Verwendung von ABAP Objects hat somit sowohl inhaltliche als auch formale Aspekte:

Hinweise und Empfehlungen zum erfolgreichen Einsatz von ABAP Objects liefert der Abschnitt Objektorientierte Programmierung.

Ausnahme

Im derzeitigen Zustand fehlen in ABAP Objects noch folgende Eigenschaften, um klassische Verarbeitungsblöcke vollständig durch Methoden zu ersetzen:

Genau für diese Fälle dürfen in neuen Programmen noch folgende klassische Verarbeitungsblöcke angelegt werden:

Innerhalb eines solchen Verarbeitungsblocks soll die Ausführung dann jedoch sofort an eine geeignete Methode delegiert werden. Diese muss keine Methode einer globalen Klasse sein, sondern kann durchaus im Rahmen einer lokalen Klasse innerhalb des zugehörigen Rahmenprogramms angesiedelt sein. Damit auch in solchen Verarbeitungsblöcken die gleiche strengere Prüfung wie in Methoden durchgeführt wird, kann in der erweiterten Programmprüfung die Prüfung Veraltete Anweisungen (OO-Kontext) eingeschaltet werden.

Schlechtes Beispiel

Folgende Quelltext enthält eine ansatzweise Implementierung der Behandlung von verschiedenen Arten von Bankkonten in einer Funktionsgruppe und deren Verwendung in einem Programm, wobei nur die Funktion "Abheben eines Betrags" gezeigt wird. Die Funktionsbausteine der Funktionsgruppe arbeiten auf externen Daten, die hier beim Ereignis LOAD-OF-PROGRAM in eine globale interne Tabelle geladen werden. Die Steuerung, ob mit einem Giro- oder Sparkonto umgegangen wird, erfolgt über einen Eingabeparameter, und die unterschiedliche Behandlung wird über eine CASE- WHEN-Kontrollstruktur an unterschiedliche Unterprogramme delegiert, wobei keine Wiederverwendung stattfindet. Die Unterprogramme greifen auf die globale interne Tabelle zu. In einem Anwendungsprogramm wird der Funktionsbaustein zum Abheben für verschiedene Konten aufgerufen. Die Ausnahmebehandlung erfolgt klassisch mit weiteren CASE-WHEN-Kontrollstrukturen für die Abfrage von sy-subrc.

FUNCTION-POOL account.
DATA account_tab TYPE SORTED TABLE OF accounts
                 WITH UNIQUE KEY id.
LOAD-OF-PROGRAM.
  "fetch amount for all accounts into account_tab
  ...
...
FUNCTION withdraw.
*"-----------------------------------------------------
*" IMPORTING
*"      REFERENCE(id) TYPE accounts-id
*"      REFERENCE(kind) TYPE c DEFAULT 'C'
*"      REFERENCE(amount) TYPE accounts-amount
*" EXCEPTIONS
*"      negative_amount
*"      unknown_account_type
*"------------------------------------------------------
  CASE kind.
    WHEN 'C'.
      PERFORM withdraw_from_checking_account
        USING id amount.
    WHEN 'S'.
      PERFORM withdraw_from_savings_account
        USING id amount.
    WHEN OTHERS.
      RAISE unknown_account_type.
  ENDCASE.
ENDFUNCTION.
FORM withdraw_from_checking_account
  USING l_id     TYPE accounts-id
        l_amount TYPE accounts-amount.
  FIELD-SYMBOLS <account> TYPE accounts.
  ASSIGN account_tab[ KEY primary_key id = l_id ] TO <account>.
  <account> = <account> - l_amount.
  IF <account> < 0.
    "Handle debit balance
    ...
  ENDIF.
ENDFORM.
FORM withdraw_from_savings_account
  USING l_id     TYPE accounts-id
        l_amount TYPE accounts-amount.
  FIELD-SYMBOLS <account> TYPE accounts.
   ASSIGN account_tab[ KEY primary_key id = l_id ] TO <account>.
  IF <account>-amount > l_amount.
     <account>-amount = <account>-amount - l_amount.
  ELSE.
     RAISE negative_amount.
  ENDIF.
ENDFORM.
*********************************************************
PROGRAM bank_application.
...
CALL FUNCTION 'WITHDRAW'
  EXPORTING
    id                   = ...
    kind                 = 'C'
    amount               = ...
  EXCEPTIONS
    unknown_account_type = 2
    negative_amount      = 4.
CASE sy-subrc.
  WHEN 2.
     ...
  WHEN 4.
     ...
ENDCASE.
...
CALL FUNCTION 'WITHDRAW'
  EXPORTING
    id                    = ...
    kind                  = 'S'
    amount                = ...
  EXCEPTIONS
    unknown_account_type  = 2
    negative_amount       = 4.
CASE sy-subrc.
  WHEN 2.
    ...
  WHEN 4.
      ...
ENDCASE.

Gutes Beispiel

Folgender Quelltext enthält eine ansatzweise Implementierung der Behandlung von verschiedenen Arten von Bankkonten in Klassen und deren Verwendung in einer Klasse, wobei wieder nur die Funktion "Abheben eines Betrags" gezeigt wird.

Die verschiedenen Kontoarten werden in Unterklassen einer abstrakten Klasse für Konten implementiert. Jede Instanz eines Kontos wird in ihrem Konstruktor genau mit den Daten versorgt, die sie benötigt. Die Anwendungsklasse erzeugt je nach Bedarf Instanzen von Konten der gewünschten Art und verwendet deren Methoden polymorph über eine Oberklassenreferenzvariable. Die Ausnahmebehandlung erfolgt über klassenbasierte Ausnahmen. Es werden keine CASE-WHEN-Kontrollstrukturen benötigt. Wie bereits bei Trennung der Belange angekündigt, entsteht hier bei der Verwendung von Klassen kein Overhead an Code mehr gegenüber der prozeduralen Programmierung.

CLASS cx_negative_amount DEFINITION PUBLIC
                         INHERITING FROM cx_static_check.
ENDCLASS.
CLASS cl_account DEFINITION ABSTRACT PUBLIC.
  PUBLIC SECTION.
    METHODS: constructor IMPORTING id     TYPE string,
             withdraw    IMPORTING amount TYPE i
                         RAISING cx_negative_amount.
  PROTECTED SECTION.
    DATA amount TYPE accounts-amount.
ENDCLASS.
CLASS cl_account IMPLEMENTATION.
  METHOD constructor.
    "fetch amount for one account into attribute amount
     ...
  ENDMETHOD.
  METHOD withdraw.
    me->amount = me->amount - amount.
  ENDMETHOD.
ENDCLASS.
CLASS cl_checking_account DEFINITION PUBLIC
                          INHERITING FROM cl_account.
  PUBLIC SECTION.
    METHODS withdraw REDEFINITION.
ENDCLASS.
CLASS cl_checking_account IMPLEMENTATION.
  METHOD withdraw.
     super->withdraw( amount ).
     IF me->amount < 0.
        "Handle debit balance
         ...
     ENDIF.
  ENDMETHOD.
ENDCLASS.
CLASS cl_savings_account DEFINITION PUBLIC
                         INHERITING FROM cl_account.
    PUBLIC SECTION.
      METHODS withdraw REDEFINITION.
ENDCLASS.
CLASS cl_savings_account IMPLEMENTATION.
  METHOD withdraw.
    IF me->amount > amount.
       super->withdraw( amount ).
    ELSE.
       RAISE EXCEPTION TYPE cx_negative_amount.
    ENDIF.
  ENDMETHOD.
ENDCLASS.
********************************************************
CLASS bank_application DEFINITION PUBLIC.
  PUBLIC SECTION.
   CLASS-METHODS main.
ENDCLASS.
CLASS bank_application IMPLEMENTATION.
  METHOD main.
    DATA: account1 TYPE REF TO cl_account,
          account2 TYPE REF TO cl_account.
  ...
CREATE OBJECT account1 TYPE cl_checking_account
  EXPORTING
    id = ...
CREATE OBJECT account2 TYPE cl_savings_account
  EXPORTING
    id = ...
...
   TRY.
      account1->withdraw( ... ).
      account2->withdraw( ... ).
    CATCH cx_negative_amount.
      ...
   ENDTRY.
  ENDMETHOD.
ENDCLASS.