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 instancesSET HANDLER <objet_handler>-><nom_methode> FOR ALL INSTANCES.Exemples
1. Événement simple
" Classe Publisher : Déclenche des événementsCLASS 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énementsCLASS 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.
" UtilisationDATA: lo_button TYPE REF TO lcl_button, lo_form TYPE REF TO lcl_form.
lo_button = NEW #( ).lo_form = NEW #( ).
" Enregistrer le handlerSET HANDLER lo_form->on_button_clicked FOR lo_button.
" Déclencher l'événementlo_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.
" UtilisationDATA: 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 EUR3. 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 handlersDATA: 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 handlersSET 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.
" UtilisationDATA: 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 classeSET 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 handlerSET HANDLER lo_notify->on_order_completed FOR lo_processor ACTIVATION space.
" Ou explicitement avec abap_falseSET HANDLER lo_logger->on_order_completed FOR lo_processor ACTIVATION abap_false.
" Réactiver le handlerSET 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.
" UtilisationDATA: 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.
" UtilisationDATA: 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 valeur8. Pattern Observer complet
" Interface SubjectINTERFACE lif_subject. METHODS: attach IMPORTING io_observer TYPE REF TO lif_observer, detach IMPORTING io_observer TYPE REF TO lif_observer, notify.ENDINTERFACE.
" Interface ObserverINTERFACE lif_observer. METHODS: update IMPORTING iv_state TYPE string.ENDINTERFACE.
" Subject concretCLASS 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 concretCLASS 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.
" UtilisationDATA: lo_station TYPE REF TO lcl_weather_station, lo_phone TYPE REF TO lcl_phone_display.
lo_station = NEW #( ).lo_phone = NEW #( ).
" Pattern Observerlo_station->lif_subject~attach( lo_phone ).
" Ou avec événementsSET 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 émetteurENDCLASS.
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.
" UtilisationDATA: 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_btn1lo_btn2->click( ). " sender = lo_btn210. 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 SALVDATA: 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
| Type | Déclaration | SET HANDLER FOR |
|---|---|---|
| Événement d’instance | EVENTS | Instance spécifique |
| Événement de classe | CLASS-EVENTS | La classe elle-même |
| Événement d’interface | EVENTS dans interface | Classe 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 INSTANCESpour 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
INTERFACEpour 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.