ABAP Events: EVENTS, RAISE EVENT und SET HANDLER

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Events ermöglichen die lose Kopplung zwischen Objekten in ABAP. Ein Objekt (Publisher) kann Ereignisse auslösen, ohne zu wissen, wer darauf reagiert. Andere Objekte (Subscriber) registrieren sich als Handler und werden bei Ereignissen benachrichtigt.

Grundkonzept

  • EVENTS: Deklariert ein Ereignis in einer Klasse
  • RAISE EVENT: Löst das Ereignis aus (Publisher)
  • SET HANDLER: Registriert eine Methode als Event-Handler (Subscriber)
  • FOR: Gibt an, für welches Objekt der Handler gilt

Syntax

Event deklarieren

CLASS <klassenname> DEFINITION.
EVENTS: <eventname>
[ EXPORTING VALUE(<parameter>) TYPE <typ> ].
ENDCLASS.

Event auslösen

RAISE EVENT <eventname>
[ EXPORTING <parameter> = <wert> ].

Handler registrieren

SET HANDLER <handler_objekt>-><methodenname>
FOR <sender_objekt>.
" Für alle Instanzen
SET HANDLER <handler_objekt>-><methodenname>
FOR ALL INSTANCES.

Beispiele

1. Einfaches Event

" Publisher-Klasse: Löst Events aus
CLASS lcl_button DEFINITION.
PUBLIC SECTION.
EVENTS: clicked.
METHODS: click.
ENDCLASS.
CLASS lcl_button IMPLEMENTATION.
METHOD click.
WRITE: / 'Button wurde geklickt'.
RAISE EVENT clicked.
ENDMETHOD.
ENDCLASS.
" Subscriber-Klasse: Reagiert auf Events
CLASS lcl_form DEFINITION.
PUBLIC SECTION.
METHODS: on_button_clicked FOR EVENT clicked OF lcl_button.
ENDCLASS.
CLASS lcl_form IMPLEMENTATION.
METHOD on_button_clicked.
WRITE: / 'Form: Button-Klick verarbeitet!'.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_button TYPE REF TO lcl_button,
lo_form TYPE REF TO lcl_form.
lo_button = NEW #( ).
lo_form = NEW #( ).
" Handler registrieren
SET HANDLER lo_form->on_button_clicked FOR lo_button.
" Event auslösen
lo_button->click( ).
" Ausgabe:
" Button wurde geklickt
" Form: Button-Klick verarbeitet!

2. Event mit Parametern

CLASS lcl_order_processor DEFINITION.
PUBLIC SECTION.
" Event mit Exportparametern
EVENTS: order_completed
EXPORTING VALUE(ev_order_id) TYPE i
VALUE(ev_amount) TYPE p DECIMALS 2.
METHODS: process_order IMPORTING iv_order_id TYPE i
iv_amount TYPE p.
ENDCLASS.
CLASS lcl_order_processor IMPLEMENTATION.
METHOD process_order.
WRITE: / |Bestellung { iv_order_id } verarbeitet.|.
" Event mit Daten auslösen
RAISE EVENT order_completed
EXPORTING
ev_order_id = iv_order_id
ev_amount = iv_amount.
ENDMETHOD.
ENDCLASS.
CLASS lcl_notification_service DEFINITION.
PUBLIC SECTION.
" Handler-Methode mit SENDER
METHODS: on_order_completed
FOR EVENT order_completed OF lcl_order_processor
IMPORTING ev_order_id ev_amount sender.
ENDCLASS.
CLASS lcl_notification_service IMPLEMENTATION.
METHOD on_order_completed.
WRITE: / |Benachrichtigung: Bestellung { ev_order_id }|.
WRITE: / |Betrag: { ev_amount } EUR|.
" sender enthält Referenz auf das auslösende Objekt
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_processor TYPE REF TO lcl_order_processor,
lo_notification TYPE REF TO lcl_notification_service.
lo_processor = NEW #( ).
lo_notification = NEW #( ).
SET HANDLER lo_notification->on_order_completed FOR lo_processor.
lo_processor->process_order( iv_order_id = 1001 iv_amount = '250.00' ).
" Ausgabe:
" Bestellung 1001 verarbeitet.
" Benachrichtigung: Bestellung 1001
" Betrag: 250.00 EUR

3. Mehrere Handler für ein Event

CLASS lcl_logger DEFINITION.
PUBLIC SECTION.
METHODS: on_order_completed
FOR EVENT order_completed OF lcl_order_processor
IMPORTING ev_order_id ev_amount.
ENDCLASS.
CLASS lcl_logger IMPLEMENTATION.
METHOD on_order_completed.
WRITE: / |LOG: Order { ev_order_id } - { ev_amount } EUR|.
ENDMETHOD.
ENDCLASS.
CLASS lcl_statistics DEFINITION.
PUBLIC SECTION.
DATA: mv_total_orders TYPE i,
mv_total_amount TYPE p DECIMALS 2.
METHODS: on_order_completed
FOR EVENT order_completed OF lcl_order_processor
IMPORTING ev_order_id ev_amount.
ENDCLASS.
CLASS lcl_statistics IMPLEMENTATION.
METHOD on_order_completed.
mv_total_orders = mv_total_orders + 1.
mv_total_amount = mv_total_amount + ev_amount.
WRITE: / |STATS: { mv_total_orders } Bestellungen, Gesamt: { mv_total_amount }|.
ENDMETHOD.
ENDCLASS.
" Verwendung - Mehrere Handler registrieren
DATA: lo_processor TYPE REF TO lcl_order_processor,
lo_notify TYPE REF TO lcl_notification_service,
lo_logger TYPE REF TO lcl_logger,
lo_statistics TYPE REF TO lcl_statistics.
lo_processor = NEW #( ).
lo_notify = NEW #( ).
lo_logger = NEW #( ).
lo_statistics = NEW #( ).
" Alle Handler registrieren
SET HANDLER: lo_notify->on_order_completed FOR lo_processor,
lo_logger->on_order_completed FOR lo_processor,
lo_statistics->on_order_completed FOR lo_processor.
lo_processor->process_order( iv_order_id = 1001 iv_amount = '100.00' ).
lo_processor->process_order( iv_order_id = 1002 iv_amount = '200.00' ).
" Alle Handler werden aufgerufen!

4. FOR ALL INSTANCES

CLASS lcl_global_logger DEFINITION.
PUBLIC SECTION.
METHODS: on_any_order_completed
FOR EVENT order_completed OF lcl_order_processor
IMPORTING ev_order_id sender.
ENDCLASS.
CLASS lcl_global_logger IMPLEMENTATION.
METHOD on_any_order_completed.
WRITE: / |Global Log: Order { ev_order_id } von Processor verarbeitet|.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_processor1 TYPE REF TO lcl_order_processor,
lo_processor2 TYPE REF TO lcl_order_processor,
lo_global_log TYPE REF TO lcl_global_logger.
lo_processor1 = NEW #( ).
lo_processor2 = NEW #( ).
lo_global_log = NEW #( ).
" Handler für ALLE Instanzen der Klasse
SET HANDLER lo_global_log->on_any_order_completed FOR ALL INSTANCES.
lo_processor1->process_order( iv_order_id = 1 iv_amount = 50 ).
lo_processor2->process_order( iv_order_id = 2 iv_amount = 75 ).
" Beide werden geloggt!

5. Handler deaktivieren

" Handler entfernen
SET HANDLER lo_notify->on_order_completed FOR lo_processor ACTIVATION space.
" Oder explizit mit abap_false
SET HANDLER lo_logger->on_order_completed
FOR lo_processor
ACTIVATION abap_false.
" Handler wieder aktivieren
SET HANDLER lo_logger->on_order_completed
FOR lo_processor
ACTIVATION abap_true.

6. Statische Events (CLASS-EVENTS)

CLASS lcl_application DEFINITION.
PUBLIC SECTION.
" Statisches Event - gehört zur Klasse, nicht zur Instanz
CLASS-EVENTS: application_started.
CLASS-METHODS: start.
ENDCLASS.
CLASS lcl_application IMPLEMENTATION.
METHOD start.
WRITE: / 'Anwendung startet...'.
RAISE EVENT application_started.
ENDMETHOD.
ENDCLASS.
CLASS lcl_startup_handler DEFINITION.
PUBLIC SECTION.
" Handler für Klassen-Event
METHODS: on_app_started
FOR EVENT application_started OF lcl_application.
ENDCLASS.
CLASS lcl_startup_handler IMPLEMENTATION.
METHOD on_app_started.
WRITE: / 'Startup-Handler: Initialisierung läuft...'.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_handler TYPE REF TO lcl_startup_handler.
lo_handler = NEW #( ).
" Bei Klassen-Events: FOR lcl_application (nicht FOR Instanz)
SET HANDLER lo_handler->on_app_started FOR lcl_application.
lcl_application=>start( ).

7. Events in Interfaces

INTERFACE lif_observable.
EVENTS: data_changed
EXPORTING VALUE(ev_new_value) TYPE string.
ENDINTERFACE.
CLASS lcl_model DEFINITION.
PUBLIC SECTION.
INTERFACES: lif_observable.
METHODS: set_value IMPORTING iv_value TYPE string.
PRIVATE SECTION.
DATA: mv_value TYPE string.
ENDCLASS.
CLASS lcl_model IMPLEMENTATION.
METHOD set_value.
mv_value = iv_value.
" Event aus Interface auslösen
RAISE EVENT lif_observable~data_changed
EXPORTING ev_new_value = mv_value.
ENDMETHOD.
ENDCLASS.
CLASS lcl_view DEFINITION.
PUBLIC SECTION.
" Handler für Interface-Event
METHODS: on_data_changed
FOR EVENT data_changed OF lif_observable
IMPORTING ev_new_value.
ENDCLASS.
CLASS lcl_view IMPLEMENTATION.
METHOD on_data_changed.
WRITE: / |View aktualisiert: { ev_new_value }|.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_model TYPE REF TO lcl_model,
lo_view TYPE REF TO lcl_view.
lo_model = NEW #( ).
lo_view = NEW #( ).
SET HANDLER lo_view->on_data_changed FOR lo_model.
lo_model->set_value( 'Neuer Wert' ).
" Ausgabe: View aktualisiert: Neuer Wert

8. Observer Pattern komplett

" Subject Interface
INTERFACE lif_subject.
METHODS: attach IMPORTING io_observer TYPE REF TO lif_observer,
detach IMPORTING io_observer TYPE REF TO lif_observer,
notify.
ENDINTERFACE.
" Observer Interface
INTERFACE lif_observer.
METHODS: update IMPORTING iv_state TYPE string.
ENDINTERFACE.
" Konkretes Subject
CLASS lcl_weather_station DEFINITION.
PUBLIC SECTION.
INTERFACES: lif_subject.
EVENTS: weather_changed EXPORTING VALUE(ev_weather) TYPE string.
METHODS: set_weather IMPORTING iv_weather TYPE string.
PRIVATE SECTION.
DATA: mt_observers TYPE TABLE OF REF TO lif_observer,
mv_weather TYPE string.
ENDCLASS.
CLASS lcl_weather_station IMPLEMENTATION.
METHOD lif_subject~attach.
APPEND io_observer TO mt_observers.
ENDMETHOD.
METHOD lif_subject~detach.
DELETE mt_observers WHERE table_line = io_observer.
ENDMETHOD.
METHOD lif_subject~notify.
LOOP AT mt_observers INTO DATA(lo_observer).
lo_observer->update( mv_weather ).
ENDLOOP.
ENDMETHOD.
METHOD set_weather.
mv_weather = iv_weather.
lif_subject~notify( ).
RAISE EVENT weather_changed EXPORTING ev_weather = mv_weather.
ENDMETHOD.
ENDCLASS.
" Konkreter Observer
CLASS lcl_phone_display DEFINITION.
PUBLIC SECTION.
INTERFACES: lif_observer.
METHODS: on_weather_changed
FOR EVENT weather_changed OF lcl_weather_station
IMPORTING ev_weather.
ENDCLASS.
CLASS lcl_phone_display IMPLEMENTATION.
METHOD lif_observer~update.
WRITE: / |Phone Display: { iv_state }|.
ENDMETHOD.
METHOD on_weather_changed.
WRITE: / |Phone Event: { ev_weather }|.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_station TYPE REF TO lcl_weather_station,
lo_phone TYPE REF TO lcl_phone_display.
lo_station = NEW #( ).
lo_phone = NEW #( ).
" Observer Pattern
lo_station->lif_subject~attach( lo_phone ).
" Oder mit Events
SET HANDLER lo_phone->on_weather_changed FOR lo_station.
lo_station->set_weather( 'Sonnig, 25°C' ).

9. SENDER Parameter

CLASS lcl_multi_button_handler DEFINITION.
PUBLIC SECTION.
METHODS: on_button_clicked
FOR EVENT clicked OF lcl_button
IMPORTING sender. " Referenz auf auslösendes Objekt
ENDCLASS.
CLASS lcl_multi_button_handler IMPLEMENTATION.
METHOD on_button_clicked.
" sender enthält die Referenz auf den geklickten Button
DATA(lo_button) = sender.
WRITE: / 'Ein Button wurde geklickt'.
" Typprüfung und Cast möglich
IF sender IS INSTANCE OF lcl_button.
" Zugriff auf Button-spezifische Methoden
ENDIF.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_btn1 TYPE REF TO lcl_button,
lo_btn2 TYPE REF TO lcl_button,
lo_handler TYPE REF TO lcl_multi_button_handler.
lo_btn1 = NEW #( ).
lo_btn2 = NEW #( ).
lo_handler = NEW #( ).
SET HANDLER lo_handler->on_button_clicked FOR ALL INSTANCES.
lo_btn1->click( ). " sender = lo_btn1
lo_btn2->click( ). " sender = lo_btn2

10. Praktisches Beispiel: ALV Event-Handling

CLASS lcl_alv_handler DEFINITION.
PUBLIC SECTION.
METHODS: on_double_click
FOR EVENT double_click OF cl_salv_events_table
IMPORTING row column.
METHODS: on_link_click
FOR EVENT link_click OF cl_salv_events_table
IMPORTING row column.
ENDCLASS.
CLASS lcl_alv_handler IMPLEMENTATION.
METHOD on_double_click.
WRITE: / |Doppelklick auf Zeile { row }, Spalte { column }|.
ENDMETHOD.
METHOD on_link_click.
WRITE: / |Link geklickt in Zeile { row }, Spalte { column }|.
ENDMETHOD.
ENDCLASS.
" Verwendung mit SALV
DATA: lo_alv TYPE REF TO cl_salv_table,
lo_events TYPE REF TO cl_salv_events_table,
lo_handler TYPE REF TO lcl_alv_handler.
TRY.
cl_salv_table=>factory(
IMPORTING r_salv_table = lo_alv
CHANGING t_table = lt_data
).
" Events holen
lo_events = lo_alv->get_event( ).
" Handler registrieren
lo_handler = NEW #( ).
SET HANDLER: lo_handler->on_double_click FOR lo_events,
lo_handler->on_link_click FOR lo_events.
lo_alv->display( ).
CATCH cx_salv_msg.
ENDTRY.

Event-Typen

TypDeklarationSET HANDLER FOR
Instanz-EventEVENTSSpezifische Instanz
Klassen-EventCLASS-EVENTSKlasse selbst
Interface-EventEVENTS in InterfaceImplementierende Klasse

Wichtige Hinweise / Best Practice

  • Events ermöglichen lose Kopplung – Publisher kennt Subscriber nicht.
  • SENDER enthält die Referenz auf das auslösende Objekt.
  • FOR ALL INSTANCES für globale Handler (Logging, Monitoring).
  • ACTIVATION space/abap_false zum Deaktivieren von Handlern.
  • Events sind synchron – Handler werden sofort ausgeführt.
  • CLASS-EVENTS für klassenweite Ereignisse ohne Instanz.
  • Nutzen Sie INTERFACE für Events bei loser Kopplung.
  • Observer Pattern ideal für UI-Updates und Benachrichtigungen.
  • Handler-Methoden müssen die richtige Signatur haben (FOR EVENT ... OF).
  • Vorsicht bei zirkulären Event-Ketten – können Endlosschleifen verursachen.