ABAP & SAP Modularisatie & OO
Modularisatie & OO

OO-ABAP: classes, interfaces & exceptions

Objectgeoriënteerd ABAP van begin tot eind: classes en visibility, constructors, interfaces, overerving en casting, events, en class-based exception handling met TRY/CATCH.

Op deze pagina

01Classes: definitie & implementatie

Object-georiënteerd ABAP (ABAP Objects) is sinds jaren de standaard voor nieuwe code. Een class bestaat altijd uit twee delen: de DEFINITION (het "wat": attributen, methodesignaturen) en de IMPLEMENTATION (het "hoe": de methodebodies). Globale classes maak je in SE24 (of Eclipse/ADT), lokale classes (lcl_) definieer je gewoon in je programma.

CLASS lcl_factuur DEFINITION.
  PUBLIC SECTION.
    METHODS:
      constructor IMPORTING iv_vbeln TYPE vbeln_vf,
      bereken_totaal RETURNING VALUE(rv_totaal) TYPE netwr,
      voeg_regel_toe IMPORTING is_regel TYPE ty_regel
                     RAISING   zcx_factuur.
    CLASS-METHODS maak_van_order
      IMPORTING iv_vbeln      TYPE vbeln_va
      RETURNING VALUE(ro_obj) TYPE REF TO lcl_factuur.
  PROTECTED SECTION.
    DATA mt_regels TYPE STANDARD TABLE OF ty_regel.
  PRIVATE SECTION.
    DATA mv_vbeln TYPE vbeln_vf.
ENDCLASS.

CLASS lcl_factuur IMPLEMENTATION.
  METHOD constructor.
    mv_vbeln = iv_vbeln.
  ENDMETHOD.
  METHOD bereken_totaal.
    rv_totaal = REDUCE netwr( INIT t TYPE netwr
                              FOR ls IN mt_regels
                              NEXT t = t + ls-bedrag ).
  ENDMETHOD.
  METHOD maak_van_order.
    ro_obj = NEW lcl_factuur( iv_vbeln = CONV #( iv_vbeln ) ).
  ENDMETHOD.
  METHOD voeg_regel_toe. " ... ENDMETHOD.
ENDCLASS.

Zichtbaarheid

SectieToegankelijk voor
PUBLIC SECTIONIedereen — dit is je API. Houd hem klein.
PROTECTED SECTIONDe class zelf + subclasses.
PRIVATE SECTIONAlleen de class zelf (en FRIENDS).

Instance vs. static

Methodeparameters & aanroepen

SoortPrefix-conventieBetekenis
IMPORTINGiv_ / is_ / it_ / io_Invoer (alleen-lezen in de methode).
EXPORTINGev_ / es_ / et_ / eo_Uitvoer. Let op: wordt niet automatisch leeggemaakt — begin met CLEAR ev_x.
CHANGINGcv_ / cs_ / ct_In/uitvoer.
RETURNING VALUE(rv_)rv_ / rs_ / rt_ / ro_Functioneel resultaat; maakt ketens en expressies mogelijk. Maximaal één, altijd by-value.
RAISINGDeclareert welke exception-classes kunnen optreden.
" Functionele aanroep — kan overal in expressies
lv_totaal = lo_factuur->bereken_totaal( ).

" Meerdere parameters
lo_factuur->voeg_regel_toe( is_regel = ls_regel ).

" EXPORTING/IMPORTING mix (aanroeperperspectief!)
lo_service->lees( EXPORTING iv_id = lv_id
                  IMPORTING es_data = ls_data ).

" Method chaining
lv_naam = zcl_klant=>lees( lv_kunnr )->get_naam( ).

02Interfaces

Een interface (globaal: ZIF_, lokaal: lif_) definieert een contract — methodesignaturen zonder implementatie. Classes die het interface opnemen, moeten alle methoden implementeren. Dit is hét mechanisme voor loskoppeling en testbaarheid (mocken in unit tests).

INTERFACE lif_printer.
  METHODS print IMPORTING iv_tekst TYPE string.
ENDINTERFACE.

CLASS lcl_pdf_printer DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_printer.
    ALIASES print FOR lif_printer~print.  " korte naam
ENDCLASS.

" Programmeren tegen het interface:
DATA lo_printer TYPE REF TO lif_printer.
lo_printer = NEW lcl_pdf_printer( ).
lo_printer->print( 'Hallo' ).

03Overerving & polymorfisme

Met INHERITING FROM erft een subclass alle niet-private componenten van de superclass, en kan hij methoden herdefiniëren.

CLASS lcl_express_order DEFINITION INHERITING FROM lcl_order.
  PUBLIC SECTION.
    METHODS bereken_kosten REDEFINITION.
ENDCLASS.

CLASS lcl_express_order IMPLEMENTATION.
  METHOD bereken_kosten.
    rv_kosten = super->bereken_kosten( ) + 10.  " superclass-versie + toeslag
  ENDMETHOD.
ENDCLASS.

04Casting: up- & downcast

Een referentie naar een subclass past altijd in een referentie naar de superclass of een interface (upcast, impliciet). De omgekeerde weg (downcast) moet expliciet en kan mislukken.

DATA lo_order TYPE REF TO lcl_order.
lo_order = NEW lcl_express_order( ).      " upcast: automatisch

" Downcast: expliciet met CAST (of oud: ?=)
TRY.
    DATA(lo_express) = CAST lcl_express_order( lo_order ).
  CATCH cx_sy_move_cast_error.
    " lo_order was géén express-order
ENDTRY.

" Veilig testen vóór de cast
IF lo_order IS INSTANCE OF lcl_express_order.
  lo_express = CAST #( lo_order ).
ENDIF.

" Of per type verwerken
CASE TYPE OF lo_order.
  WHEN TYPE lcl_express_order INTO DATA(lo_exp). " ...
  WHEN OTHERS. " ...
ENDCASE.

05Events

Classes kunnen gebeurtenissen publiceren waar andere objecten zich op abonneren — veel gebruikt in GUI-programmering (ALV-dubbelklik bijvoorbeeld).

" In de publicerende class:
EVENTS voltooid EXPORTING VALUE(ev_id) TYPE i.
...
RAISE EVENT voltooid EXPORTING ev_id = 42.

" In de luisterende class:
METHODS on_voltooid FOR EVENT voltooid OF lcl_taak
  IMPORTING ev_id.

" Koppelen:
SET HANDLER lo_listener->on_voltooid FOR lo_taak.
SET HANDLER lo_listener->on_voltooid FOR ALL INSTANCES.

06Class-based exceptions: TRY/CATCH

Moderne foutafhandeling gebruikt exception-classes (CX_, eigen: ZCX_) in plaats van sy-subrc-codes.

TRY.
    DATA(lo_factuur) = zcl_factuur=>lees( iv_vbeln = lv_vbeln ).
    lo_factuur->boek( ).

  CATCH zcx_niet_gevonden INTO DATA(lx_nf).
    MESSAGE lx_nf->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.

  CATCH zcx_factuur cx_sy_arithmetic_error INTO DATA(lx_alg).
    " meerdere types in één CATCH

  CLEANUP.
    " opruimen als de exception hogerop wordt afgehandeld
ENDTRY.

" Zelf gooien:
RAISE EXCEPTION NEW zcx_niet_gevonden( vbeln = lv_vbeln ).
" of klassiek: RAISE EXCEPTION TYPE zcx_niet_gevonden EXPORTING vbeln = ...

De drie basisklassen

SuperclassDeclaratieplicht (RAISING)Gebruik voor
CX_STATIC_CHECKJa — compiler dwingt declaratie of afvangen afVerwachtbare businessfouten ("order niet gevonden"). Default.
CX_DYNAMIC_CHECKNee (check pas at runtime)Fouten die de aanroeper meestal kan uitsluiten (delen door nul).
CX_NO_CHECKNee, mag ook nietOnherstelbare systeemfouten; propageren altijd.

07ABAP Unit: testen

Unit tests schrijf je als lokale testclasses (FOR TESTING) bij je programma of globale class; uitvoeren met Ctrl+Shift+F10 (SE80/ADT).

CLASS ltc_factuur DEFINITION FINAL FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    METHODS totaal_van_lege_factuur_is_nul FOR TESTING.
    METHODS setup.   " draait vóór elke test
    DATA mo_cut TYPE REF TO lcl_factuur.  " cut = code under test
ENDCLASS.

CLASS ltc_factuur IMPLEMENTATION.
  METHOD setup.
    mo_cut = NEW lcl_factuur( '0000000001' ).
  ENDMETHOD.
  METHOD totaal_van_lege_factuur_is_nul.
    cl_abap_unit_assert=>assert_equals(
      act = mo_cut->bereken_totaal( )
      exp = 0
      msg = 'Lege factuur moet totaal 0 hebben' ).
  ENDMETHOD.
ENDCLASS.

Veelgebruikte asserts: assert_equals, assert_true, assert_initial, assert_bound, fail. Afhankelijkheden (database, tijd) isoleer je via interfaces en test doubles; voor database-toegang bestaat bovendien CL_OSQL_TEST_ENVIRONMENT.