Événements ABAP : EVENTS, RAISE EVENT et SET HANDLER

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

Les événements permettent un couplage faible entre les objets en ABAP. Un objet (Publisher) peut déclencher des événements sans savoir qui y réagit. D’autres objets (Subscribers) s’enregistrent comme handlers et sont notifiés lors des événements.

Concept de base

  • EVENTS : Déclare un événement dans une classe
  • RAISE EVENT : Déclenche l’événement (Publisher)
  • SET HANDLER : Enregistre une méthode comme Event-Handler (Subscriber)
  • FOR : Indique pour quel objet le handler s’applique

Syntaxe

Déclarer un événement

CLASS <nom_classe> DEFINITION.
EVENTS: <nom_evenement>
[ EXPORTING VALUE(<parametre>) TYPE <type> ].
ENDCLASS.

Déclencher un événement

RAISE EVENT <nom_evenement>
[ EXPORTING <parametre> = <valeur> ].

Enregistrer un handler

SET HANDLER <objet_handler>-><nom_methode>
FOR <objet_emetteur>.
" Pour toutes les instances
SET HANDLER <objet_handler>-><nom_methode>
FOR ALL INSTANCES.

Exemples

1. Événement simple

" Classe Publisher : Déclenche des événements
CLASS lcl_button DEFINITION.
PUBLIC SECTION.
EVENTS: clicked.
METHODS: click.
ENDCLASS.
CLASS lcl_button IMPLEMENTATION.
METHOD click.
WRITE: / 'Le bouton a été cliqué'.
RAISE EVENT clicked.
ENDMETHOD.
ENDCLASS.
" Classe Subscriber : Réagit aux événements
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 : Clic de bouton traité !'.
ENDMETHOD.
ENDCLASS.
" Utilisation
DATA: lo_button TYPE REF TO lcl_button,
lo_form TYPE REF TO lcl_form.
lo_button = NEW #( ).
lo_form = NEW #( ).
" Enregistrer le handler
SET HANDLER lo_form->on_button_clicked FOR lo_button.
" Déclencher l'événement
lo_button->click( ).
" Sortie :
" Le bouton a été cliqué
" Form : Clic de bouton traité !

2. Événement avec paramètres

CLASS lcl_order_processor DEFINITION.
PUBLIC SECTION.
" Événement avec paramètres d'export
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: / |Commande { iv_order_id } traitée.|.
" Déclencher l'événement avec les données
RAISE EVENT order_completed
EXPORTING
ev_order_id = iv_order_id
ev_amount = iv_amount.
ENDMETHOD.
ENDCLASS.
CLASS lcl_notification_service DEFINITION.
PUBLIC SECTION.
" Méthode handler avec 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: / |Notification : Commande { ev_order_id }|.
WRITE: / |Montant : { ev_amount } EUR|.
" sender contient la référence à l'objet émetteur
ENDMETHOD.
ENDCLASS.
" Utilisation
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' ).
" Sortie :
" Commande 1001 traitée.
" Notification : Commande 1001
" Montant : 250.00 EUR

3. Plusieurs handlers pour un événement

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 : Commande { 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 } commandes, Total : { mv_total_amount }|.
ENDMETHOD.
ENDCLASS.
" Utilisation - Enregistrer plusieurs handlers
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 #( ).
" Enregistrer tous les handlers
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' ).
" Tous les handlers sont appelés !

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: / |Log global : Commande { ev_order_id } traitée par Processor|.
ENDMETHOD.
ENDCLASS.
" Utilisation
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 pour TOUTES les instances de la classe
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 ).
" Les deux sont loggés !

5. Désactiver un handler

" Retirer le handler
SET HANDLER lo_notify->on_order_completed FOR lo_processor ACTIVATION space.
" Ou explicitement avec abap_false
SET HANDLER lo_logger->on_order_completed
FOR lo_processor
ACTIVATION abap_false.
" Réactiver le handler
SET HANDLER lo_logger->on_order_completed
FOR lo_processor
ACTIVATION abap_true.

6. Événements statiques (CLASS-EVENTS)

CLASS lcl_application DEFINITION.
PUBLIC SECTION.
" Événement statique - appartient à la classe, pas à l'instance
CLASS-EVENTS: application_started.
CLASS-METHODS: start.
ENDCLASS.
CLASS lcl_application IMPLEMENTATION.
METHOD start.
WRITE: / 'Application démarre...'.
RAISE EVENT application_started.
ENDMETHOD.
ENDCLASS.
CLASS lcl_startup_handler DEFINITION.
PUBLIC SECTION.
" Handler pour événement de classe
METHODS: on_app_started
FOR EVENT application_started OF lcl_application.
ENDCLASS.
CLASS lcl_startup_handler IMPLEMENTATION.
METHOD on_app_started.
WRITE: / 'Startup-Handler : Initialisation en cours...'.
ENDMETHOD.
ENDCLASS.
" Utilisation
DATA: lo_handler TYPE REF TO lcl_startup_handler.
lo_handler = NEW #( ).
" Pour les événements de classe : FOR lcl_application (pas FOR instance)
SET HANDLER lo_handler->on_app_started FOR lcl_application.
lcl_application=>start( ).

7. Événements dans les 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.
" Déclencher l'événement depuis l'interface
RAISE EVENT lif_observable~data_changed
EXPORTING ev_new_value = mv_value.
ENDMETHOD.
ENDCLASS.
CLASS lcl_view DEFINITION.
PUBLIC SECTION.
" Handler pour événement d'interface
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: / |Vue mise à jour : { ev_new_value }|.
ENDMETHOD.
ENDCLASS.
" Utilisation
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( 'Nouvelle valeur' ).
" Sortie : Vue mise à jour : Nouvelle valeur

8. Pattern Observer complet

" Interface Subject
INTERFACE lif_subject.
METHODS: attach IMPORTING io_observer TYPE REF TO lif_observer,
detach IMPORTING io_observer TYPE REF TO lif_observer,
notify.
ENDINTERFACE.
" Interface Observer
INTERFACE lif_observer.
METHODS: update IMPORTING iv_state TYPE string.
ENDINTERFACE.
" Subject concret
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.
" Observer concret
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: / |Écran téléphone : { iv_state }|.
ENDMETHOD.
METHOD on_weather_changed.
WRITE: / |Événement téléphone : { ev_weather }|.
ENDMETHOD.
ENDCLASS.
" Utilisation
DATA: lo_station TYPE REF TO lcl_weather_station,
lo_phone TYPE REF TO lcl_phone_display.
lo_station = NEW #( ).
lo_phone = NEW #( ).
" Pattern Observer
lo_station->lif_subject~attach( lo_phone ).
" Ou avec événements
SET HANDLER lo_phone->on_weather_changed FOR lo_station.
lo_station->set_weather( 'Ensoleillé, 25°C' ).

9. Paramètre SENDER

CLASS lcl_multi_button_handler DEFINITION.
PUBLIC SECTION.
METHODS: on_button_clicked
FOR EVENT clicked OF lcl_button
IMPORTING sender. " Référence à l'objet émetteur
ENDCLASS.
CLASS lcl_multi_button_handler IMPLEMENTATION.
METHOD on_button_clicked.
" sender contient la référence au bouton cliqué
DATA(lo_button) = sender.
WRITE: / 'Un bouton a été cliqué'.
" Vérification de type et cast possibles
IF sender IS INSTANCE OF lcl_button.
" Accès aux méthodes spécifiques du bouton
ENDIF.
ENDMETHOD.
ENDCLASS.
" Utilisation
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. Exemple pratique : Gestion des événements ALV

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: / |Double-clic sur ligne { row }, colonne { column }|.
ENDMETHOD.
METHOD on_link_click.
WRITE: / |Lien cliqué sur ligne { row }, colonne { column }|.
ENDMETHOD.
ENDCLASS.
" Utilisation avec 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
).
" Récupérer les événements
lo_events = lo_alv->get_event( ).
" Enregistrer les handlers
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.

Types d’événements

TypeDéclarationSET HANDLER FOR
Événement d’instanceEVENTSInstance spécifique
Événement de classeCLASS-EVENTSLa classe elle-même
Événement d’interfaceEVENTS dans interfaceClasse implémentant l’interface

Remarques importantes / Bonnes pratiques

  • Les événements permettent un couplage faible – le Publisher ne connaît pas le Subscriber.
  • SENDER contient la référence à l’objet émetteur.
  • FOR ALL INSTANCES pour les handlers globaux (logging, monitoring).
  • ACTIVATION space/abap_false pour désactiver les handlers.
  • Les événements sont synchrones – les handlers sont exécutés immédiatement.
  • CLASS-EVENTS pour les événements au niveau de la classe sans instance.
  • Utilisez INTERFACE pour les événements avec couplage faible.
  • Le pattern Observer est idéal pour les mises à jour UI et les notifications.
  • Les méthodes handler doivent avoir la bonne signature (FOR EVENT ... OF).
  • Attention aux chaînes d’événements circulaires – peuvent causer des boucles infinies.