ABAP-Entwicklung

In diesem Blogartikel geht es um selbst definierte Makros in ABAP. Sie erfahren, was Makros sind, was Sie bei der Implementierung beachten müssen und wann Sie Makros einsetzen können.

Makros sind Zusammenfassungen von Befehlen, die an bestimmten Stellen im Programmcode eingefügt werden können. Anders als Methoden oder Forms wird jedoch keine andere Speicherstelle angesprungen, sondern der Inhalt des Makros wird an die Stelle, an der das Makro aufgerufen wird, eingefügt. Makros sind also einfache Textersetzungen, die vor dem eigentlichen Aktivieren stattfinden.

Das bedeutet, für das SAP-System steht an der Stelle, an der das Makro aufgerufen wird, der Code, der im Makro definiert wird. Oder anders herum, wenn Sie eine bestimmte Programmzeile in ein Makro auslagern, entsteht nach dem Aktivieren trotzdem exakt derselbe Bytecode.

Einsatzzweck

Da Makros eine einfache Textersetzung darstellen, sind sie, anders als Forms oder Methoden, nicht für größere Codeblöcke geeignet. Der Einsatzzweck für Makros sind daher kleine Operationen, die genauso gut Befehle sein können. Ein Beispiel dafür:

MOVE-CORRESPONDING: lt_a TO lt_b.

Den Befehl kann man sich sehr gut als eine Operation vorstellen. Alle Zeilen der Tabelle lt_a werden nach lt_b kopiert. Dabei wird darauf geachtet, dass die Inhalte von Feld a auch nach Feld a in der Zieltabelle kopiert werden. Man könnte dies auch mittels folgendem Code erreichen:

DATA: lo_strucdescr_a TYPE REF TO cl_abap_structdescr.

lo_strucdescr_a ?= cast cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( lt_a ) 
                   )->get_table_line_type( ).

LOOP AT lt_a ASSIGNING FIELD-SYMBOL(<ls_a>).    
  APPEND INITIAL LINE TO lt_b ASSIGNING FIELD-SYMBOL(<ls_b>).
  LOOP AT lo_strucdescr_a->get_components( ) INTO DATA(ls_comp).
    ASSIGN COMPONENT ls_comp-name OF STRUCTURE <ls_a> TO FIELD-SYMBOL(<lv_a>).
    ASSIGN COMPONENT ls_comp-name OF STRUCTURE <ls_b> TO field-symbol(<lv_b>).
    IF <ls_a> IS ASSIGNED AND
       <ls_b> IS ASSIGNED.
      TRY.
        <lv_b> = <lv_a>.
      CATCH cx_root.
      ENDTRY.
    ENDIF.
    UNASSIGN: <lv_a>, <lv_b>.
  ENDLOOP.
ENDLOOP.

Ich glaube wir alle sind ganz froh, dass es MOVE-CORRESPONDING gibt. Zumal der obere Code nur mit flachen Strukturen und ohne Includes funktioniert. Eine korrekte Implementierung von MOVE-CORRESPONDING wäre noch deutlich umfangreicher.

Makro oder Methode

Aufgrund des Umfangs des oberen Code wäre die Implementierung eines MOVE-CORRESPONDING Befehls als Methode sicherlich sinnvoller als die Implementierung über ein Makro. Aber auch aus praktischen Gründen kann der obere Code nicht sinnvoll als Makro verwendet werden.

  1. Der obere Code enthält Datendefinitionen (DATA und FIELD-SYMBOLS). Da es sich bei Makros um eine Textersetzung handelt, könnte dieses Makro nur einmal in einer Methode verwendet werden. Ansonsten würde z.B. das Objekt lo_strucdescr_a dort ja zweimal definiert wird.
  2. Zu umfangreich: Hier wird zwar nur eine Operation abgebildet, sollte jedoch etwas “schiefgehen”, kann der Fehler nur schwer identifiziert werden, da Makros nicht gedebuggt werden können.

Ein gutes Makro

Für Makros kann man daher folgende Regeln aufstellen:

  1. Ein gutes Makro enthält keine Datendefinitionen mittels DATA, FIELD-SYMBOLS, STATICS, LOCAL, CONSTANTS usw.
  2. Ein gutes Makro enthält keine Typdefinitionen mittels TYPES, CLASS etc.
  3. Ein gutes Makro ist überschaubar und als einzelne Operation begreifbar
  4. Ein gutes Makro setzt keine deklarierten Variablen voraus, damit es universell einsetzbar ist
    1. Ausnahme: Makros, die in lokalen Methoden definiert sind und daher ausschließlich dort verwendet werden

Darüberhinaus sollte ein Makro gut dokumentiert sein

  1. Was wird hier gemacht
  2. Was sind die Übergabeparameter
  3. Aussagekräftiger Name

Da Makros nicht gedebuggt werden können, sollten Makros außerdem

  1. Keine weiteren Makros aufrufen
  2. Sehr ausführlich getestet werden, sodass auf die Funktionsfähigkeit verlass ist

Außerdem sollten Makros gut dosiert eingesetzt werden. Da andere Entwickler ebenfalls mit dem Code in Kontakt kommen könnten, wäre es nicht gut, wenn diese die Funktionsweise von 30 verschiedene Makros erlernen müssen, um den Code zu verstehen.

Makros dienen also nicht der Strukturierung von Code, sondern dem “übersichtlicher machen” von Code, indem größere Codefragmente die wiederkehrend aufgerufen werden zu einem Makro zusammengefasst werden.

Beispiele

Checkbox

Checkboxes werden auf dem Selektionsbild mittels PARAMETERS definiert. Soll die Checkbox jedoch nicht wie im Standard links, sondern auf Höhe der Eingabefelder erscheinen, sind diverse Anweisungen notwendig, die letztlich nichts anderes tun, als eine Checkbox darzustellen. Dieser einfache Sachverhalt kann im dadurch notwendigen “Code-Wust” schwer erkennbar sein. Hier lohnt sich eine Vereinfachung mittels Makro:

"
" Gebe eine Checkbox aus, die sich rechts, auf Höhe
" der sonstigen Eingabefelder befindet
"
DEFINE checkbox. "Name der Checkbox, Text
  SELECTION-SCREEN BEGIN OF LINE.
    SELECTION-SCREEN COMMENT 1(30)        &2.
    SELECTION-SCREEN POSITION 33.
    PARAMETERS:       &1            AS CHECKBOX.
  SELECTION-SCREEN END   OF LINE.
END-OF-DEFINITION.

Im Selektionsbild können hierdurch folgendermaßen Checkboxes ausgegeben werden:

checkbox p_opt01 text-p01.
checkbox p_opt02 text-p02.
checkbox p_opt03 text-p03.
checkbox p_opt04 text-p04.

Ein Beispielprogramm könnte so aussehen:

Beispiel Checkboxes

Der entsprechende Code dazu:

Coding zu den Checkboxes

Texte im ALV

Ein Beispiel für ein Makro, welches nur lokal in einer Methode verwendet werden sollte, ist das Setzen von Texten für ALV-Spalten.

"
" Setze Kurz-, Mittel- und Langtext einer Spalte im ALV
"
DEFINE set_avl_col_text. "Spaltenname, Kurztext, Mitteltext, Langtext
  TRY.
    lo_columns->get_column( &1 )->set_short_text( CONV scrtext_s( &2 ) ).
    lo_columns->get_column( &1 )->set_medium_text( CONV scrtext_m( &3 ) ).
    lo_columns->get_column( &1 )->set_long_text( CONV scrtext_l( &4 ) ).
  CATCH cx_static_check.
    "... Fehlerbehandlung
  ENDTRY.
END-OF-DEFINITION.

Aufgerufen wird das Makro über folgenden Code:

set_alv_col_text 'PERNR' text-s01 text-m01 text-l01.

Da dieses Makro jedoch die Existenz eines Objekts lo_columns vom Typ CL_SALV_COLUMNS voraussetzt, sollte dieses Makro ausschließlich lokal in der verwendeten Methode definiert und dokumentiert sein. Der Übersichtlichkeit wegen sollte dies am Anfang der Methode passieren.

Fazit

Makros sind zwar nur in sehr speziellen Situationen sinnvoll, sollten nicht zu häufig und mit Bedacht eingesetzt werden, können den Code an entscheidenden Stellen jedoch stark vereinfachen. Wenn – insbesondere in anspruchsvollen Routinen – eine Vielzahl von Befehlen, die von Menschen als eine Operation begreifbar sind, durch ein Makro ersetzt werden, kann dies die Übersichtlichkeit des Codes stark verbessern.

Aber auch für triviale Programmteile, wie das Setzen der obigen Checkboxes sind Makros sinnvoll. Spätestens wenn in einem Report bspw. 10 solcher Checkboxes nötig sind, ist der Vorteil klar ersichtlich.


Noch Fragen?

Bei weiteren Fragen können Sie uns jederzeit gerne ansprechen. Einen direkten Link zum Kontaktformular finden Sie hier. Wir freuen uns auf Ihre Nachricht!

Oder gibt es ein Thema, was Sie „brennend“ interessiert und worüber wir unbedingt schreiben sollten?

Dann schicken Sie uns Ihre Idee gerne per Mail über das folgende Kontaktformular! Vielen Dank im Voraus!