Ein INTERFACE in ABAP definiert einen Vertrag (Contract), den Klassen erfüllen müssen. Es legt fest, welche Methoden eine Klasse bereitstellen muss, ohne deren Implementierung vorzugeben.
Grundkonzept
- Ein Interface definiert nur die Signaturen von Methoden (und optional Attribute/Konstanten).
- Eine Klasse implementiert das Interface und liefert die konkrete Logik.
- Polymorphismus: Verschiedene Klassen können das gleiche Interface implementieren, aber unterschiedlich reagieren.
Syntax
Interface definieren
INTERFACE <interfacename>. METHODS: <methode1> [IMPORTING/EXPORTING/RETURNING ...], <methode2> [...]. DATA: <attribut> TYPE <typ>. CONSTANTS: <konstante> TYPE <typ> VALUE <wert>.ENDINTERFACE.Interface in Klasse implementieren
CLASS <klassenname> DEFINITION. PUBLIC SECTION. INTERFACES: <interfacename>. " Optional: Alias für Interface-Methoden ALIASES: <alias> FOR <interfacename>~<methode>.ENDCLASS.
CLASS <klassenname> IMPLEMENTATION. METHOD <interfacename>~<methode>. " Implementierung ENDMETHOD.ENDCLASS.Beispiele
1. Einfaches Interface
" Interface definierenINTERFACE lif_printable. METHODS: print RETURNING VALUE(rv_output) TYPE string.ENDINTERFACE.
" Klasse 1: DokumentCLASS lcl_document DEFINITION. PUBLIC SECTION. INTERFACES: lif_printable. METHODS: constructor IMPORTING iv_title TYPE string. PRIVATE SECTION. DATA: mv_title TYPE string.ENDCLASS.
CLASS lcl_document IMPLEMENTATION. METHOD constructor. mv_title = iv_title. ENDMETHOD.
METHOD lif_printable~print. rv_output = |Dokument: { mv_title }|. ENDMETHOD.ENDCLASS.
" Klasse 2: RechnungCLASS lcl_invoice DEFINITION. PUBLIC SECTION. INTERFACES: lif_printable. METHODS: constructor IMPORTING iv_number TYPE string iv_amount TYPE p. PRIVATE SECTION. DATA: mv_number TYPE string, mv_amount TYPE p DECIMALS 2.ENDCLASS.
CLASS lcl_invoice IMPLEMENTATION. METHOD constructor. mv_number = iv_number. mv_amount = iv_amount. ENDMETHOD.
METHOD lif_printable~print. rv_output = |Rechnung { mv_number }: { mv_amount } EUR|. ENDMETHOD.ENDCLASS.
" Verwendung mit PolymorphismusDATA: lt_printables TYPE TABLE OF REF TO lif_printable, lo_printable TYPE REF TO lif_printable.
" Verschiedene Objekte in eine ListeAPPEND NEW lcl_document( 'Vertrag' ) TO lt_printables.APPEND NEW lcl_invoice( iv_number = 'R-001' iv_amount = '1500.00' ) TO lt_printables.
" Alle gleich behandelnLOOP AT lt_printables INTO lo_printable. WRITE: / lo_printable->print( ).ENDLOOP.
" Ausgabe:" Dokument: Vertrag" Rechnung R-001: 1500.00 EUR2. Interface mit Alias
Mit ALIASES können Sie kürzere Namen für Interface-Methoden vergeben:
INTERFACE lif_calculator. METHODS: calculate IMPORTING iv_a TYPE i iv_b TYPE i RETURNING VALUE(rv_result) TYPE i.ENDINTERFACE.
CLASS lcl_adder DEFINITION. PUBLIC SECTION. INTERFACES: lif_calculator. " Alias für einfacheren Zugriff ALIASES: add FOR lif_calculator~calculate.ENDCLASS.
CLASS lcl_adder IMPLEMENTATION. METHOD lif_calculator~calculate. rv_result = iv_a + iv_b. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_adder TYPE REF TO lcl_adder.lo_adder = NEW #( ).
" Mit vollständigem NamenDATA(lv_result1) = lo_adder->lif_calculator~calculate( iv_a = 5 iv_b = 3 ).
" Mit Alias (kürzer)DATA(lv_result2) = lo_adder->add( iv_a = 5 iv_b = 3 ).3. Mehrere Interfaces implementieren
Eine Klasse kann mehrere Interfaces implementieren:
INTERFACE lif_serializable. METHODS: to_json RETURNING VALUE(rv_json) TYPE string.ENDINTERFACE.
INTERFACE lif_comparable. METHODS: compare_to IMPORTING io_other TYPE REF TO lif_comparable RETURNING VALUE(rv_result) TYPE i.ENDINTERFACE.
CLASS lcl_product DEFINITION. PUBLIC SECTION. INTERFACES: lif_serializable, lif_comparable. METHODS: constructor IMPORTING iv_id TYPE i iv_name TYPE string iv_price TYPE p. DATA: mv_id TYPE i READ-ONLY, mv_name TYPE string READ-ONLY, mv_price TYPE p DECIMALS 2 READ-ONLY.ENDCLASS.
CLASS lcl_product IMPLEMENTATION. METHOD constructor. mv_id = iv_id. mv_name = iv_name. mv_price = iv_price. ENDMETHOD.
METHOD lif_serializable~to_json. rv_json = |{ "id": { mv_id }, "name": "{ mv_name }", "price": { mv_price } }|. ENDMETHOD.
METHOD lif_comparable~compare_to. DATA: lo_other TYPE REF TO lcl_product. lo_other ?= io_other. IF mv_price < lo_other->mv_price. rv_result = -1. ELSEIF mv_price > lo_other->mv_price. rv_result = 1. ELSE. rv_result = 0. ENDIF. ENDMETHOD.ENDCLASS.4. Interface mit Konstanten und Attributen
INTERFACE lif_status. CONSTANTS: c_new TYPE i VALUE 1, c_active TYPE i VALUE 2, c_completed TYPE i VALUE 3, c_cancelled TYPE i VALUE 4.
DATA: mv_current_status TYPE i.
METHODS: set_status IMPORTING iv_status TYPE i, get_status RETURNING VALUE(rv_status) TYPE i.ENDINTERFACE.
CLASS lcl_order DEFINITION. PUBLIC SECTION. INTERFACES: lif_status.ENDCLASS.
CLASS lcl_order IMPLEMENTATION. METHOD lif_status~set_status. lif_status~mv_current_status = iv_status. ENDMETHOD.
METHOD lif_status~get_status. rv_status = lif_status~mv_current_status. ENDMETHOD.ENDCLASS.
" Verwendung der Interface-KonstantenDATA: lo_order TYPE REF TO lcl_order.lo_order = NEW #( ).
lo_order->lif_status~set_status( lif_status=>c_active ).5. Interface-Vererbung (Composed Interface)
Interfaces können andere Interfaces einschließen:
INTERFACE lif_readable. METHODS: read RETURNING VALUE(rv_data) TYPE string.ENDINTERFACE.
INTERFACE lif_writable. METHODS: write IMPORTING iv_data TYPE string.ENDINTERFACE.
" Zusammengesetztes InterfaceINTERFACE lif_read_write. INTERFACES: lif_readable, lif_writable. METHODS: clear.ENDINTERFACE.
" Eine Klasse, die lif_read_write implementiert," muss alle Methoden aus allen drei Interfaces implementierenCLASS lcl_buffer DEFINITION. PUBLIC SECTION. INTERFACES: lif_read_write. PRIVATE SECTION. DATA: mv_buffer TYPE string.ENDCLASS.
CLASS lcl_buffer IMPLEMENTATION. METHOD lif_readable~read. rv_data = mv_buffer. ENDMETHOD.
METHOD lif_writable~write. mv_buffer = iv_data. ENDMETHOD.
METHOD lif_read_write~clear. CLEAR mv_buffer. ENDMETHOD.ENDCLASS.6. Dependency Injection mit Interfaces
Interfaces ermöglichen lose Kopplung und bessere Testbarkeit:
" Interface für DatenbankzugriffINTERFACE lif_customer_repository. METHODS: get_customer IMPORTING iv_id TYPE i RETURNING VALUE(rs_customer) TYPE zcustomer RAISING cx_not_found.ENDINTERFACE.
" Produktive ImplementierungCLASS lcl_db_customer_repository DEFINITION. PUBLIC SECTION. INTERFACES: lif_customer_repository.ENDCLASS.
CLASS lcl_db_customer_repository IMPLEMENTATION. METHOD lif_customer_repository~get_customer. SELECT SINGLE * FROM zcustomer WHERE id = @iv_id INTO @rs_customer. IF sy-subrc <> 0. RAISE EXCEPTION TYPE cx_not_found. ENDIF. ENDMETHOD.ENDCLASS.
" Mock für Unit TestsCLASS lcl_mock_customer_repository DEFINITION. PUBLIC SECTION. INTERFACES: lif_customer_repository.ENDCLASS.
CLASS lcl_mock_customer_repository IMPLEMENTATION. METHOD lif_customer_repository~get_customer. rs_customer = VALUE #( id = iv_id name = 'Test Customer' ). ENDMETHOD.ENDCLASS.
" Service nutzt das Interface (nicht die konkrete Klasse)CLASS lcl_customer_service DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING io_repository TYPE REF TO lif_customer_repository, get_customer_name IMPORTING iv_id TYPE i RETURNING VALUE(rv_name) TYPE string. PRIVATE SECTION. DATA: mo_repository TYPE REF TO lif_customer_repository.ENDCLASS.
CLASS lcl_customer_service IMPLEMENTATION. METHOD constructor. mo_repository = io_repository. ENDMETHOD.
METHOD get_customer_name. TRY. DATA(ls_customer) = mo_repository->get_customer( iv_id ). rv_name = ls_customer-name. CATCH cx_not_found. rv_name = 'Unbekannt'. ENDTRY. ENDMETHOD.ENDCLASS.Interface vs. Abstrakte Klasse
| Aspekt | Interface | Abstrakte Klasse |
|---|---|---|
| Mehrfachvererbung | Ja (mehrere Interfaces) | Nein (nur eine Elternklasse) |
| Implementierung | Keine | Kann teilweise implementieren |
| Attribute | Ja (aber unüblich) | Ja |
| Konstruktor | Nein | Ja |
| Anwendung | Verträge, Polymorphismus | Gemeinsame Basislogik |
Typprüfung und Casting
DATA: lo_object TYPE REF TO object, lo_printable TYPE REF TO lif_printable.
lo_object = NEW lcl_document( 'Test' ).
" Prüfen, ob Interface implementiert wirdIF lo_object IS INSTANCE OF lif_printable. " Downcast zum Interface lo_printable ?= lo_object. WRITE: / lo_printable->print( ).ENDIF.Wichtige Hinweise / Best Practice
- Interfaces beginnen oft mit
lif_(lokal) oderzif_/if_(global). - Nutzen Sie Interfaces für lose Kopplung und bessere Testbarkeit.
- Bevorzugen Sie Interfaces über Vererbung für Polymorphismus.
- Halten Sie Interfaces klein und fokussiert (Interface Segregation Principle).
- Verwenden Sie
ALIASESfür häufig genutzte Interface-Methoden. - Interface-Konstanten sind nützlich für Status-Codes und Enumerationen.
- Kombinieren Sie Interfaces mit
TRY...CATCHfür robuste Fehlerbehandlung. - Nutzen Sie
CLASSfür die Implementierung von Interfaces.