Observer
Observer — поведінковий патерн: обʼєкт-subject підтримує список спостерігачів і сповіщає їх про зміни свого стану. У ABAP для цього є вбудовані events — класовий механізм, що звільняє від необхідності руками писати реєстр спостерігачів.
Метафора
Section titled “Метафора”Підписка на канал: автор публікує пост → всі підписники отримують сповіщення. Автор не знає, хто саме підписаний — він просто робить publish. Підписник не знає, як саме автор стежить за ним — він просто отримує сповіщення на SET HANDLER.
Коли застосовувати
Section titled “Коли застосовувати”- Зміна стану одного обʼєкта має тригерити побічні дії в кількох інших (лог, аудит, оновлення UI, відправка notification).
- Обʼєкт-джерело не повинен знати про конкретних отримувачів — тільки факт «щось трапилось».
- Потрібна динамічна підписка/відписка — у runtime.
- Хочеш уникнути прямих залежностей
subject → observer1, observer2, observer3.
ABAP-реалізація — через events
Section titled “ABAP-реалізація — через events”ABAP events — найчистіша форма Observer у мові. Обʼєкт-джерело декларує EVENTS, підписники реєструють handler через SET HANDLER.
CLASS zcl_order DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. EVENTS: confirmed EXPORTING VALUE(ev_order_id) TYPE zty_order_id, cancelled EXPORTING VALUE(ev_order_id) TYPE zty_order_id VALUE(ev_reason) TYPE string.
METHODS: constructor IMPORTING iv_id TYPE zty_order_id, confirm, cancel IMPORTING iv_reason TYPE string.
PRIVATE SECTION. DATA mv_id TYPE zty_order_id.ENDCLASS.
CLASS zcl_order IMPLEMENTATION. METHOD constructor. mv_id = iv_id. ENDMETHOD.
METHOD confirm. " ... бізнес-логіка ... RAISE EVENT confirmed EXPORTING ev_order_id = mv_id. ENDMETHOD.
METHOD cancel. " ... бізнес-логіка ... RAISE EVENT cancelled EXPORTING ev_order_id = mv_id ev_reason = iv_reason. ENDMETHOD.ENDCLASS.Підписник (наприклад, аудит-логер):
CLASS zcl_audit_logger DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS: on_confirmed FOR EVENT confirmed OF zcl_order IMPORTING ev_order_id, on_cancelled FOR EVENT cancelled OF zcl_order IMPORTING ev_order_id ev_reason.ENDCLASS.
CLASS zcl_audit_logger IMPLEMENTATION. METHOD on_confirmed. " INSERT zaudit VALUES ( order = ev_order_id action = 'CONFIRM' ts = ... ) ENDMETHOD. METHOD on_cancelled. " INSERT zaudit VALUES ( ... action = 'CANCEL' reason = ev_reason ) ENDMETHOD.ENDCLASS.Підключення:
DATA(lo_order) = NEW zcl_order( iv_id = '4711' ).DATA(lo_logger) = NEW zcl_audit_logger( ).
SET HANDLER lo_logger->on_confirmed FOR lo_order.SET HANDLER lo_logger->on_cancelled FOR lo_order.
lo_order->confirm( ). " → lo_logger->on_confirmed виконаєтьсяВідписка: SET HANDLER lo_logger->on_confirmed FOR lo_order ACTIVATION abap_false.
Або одразу для всіх субʼєктів класу: SET HANDLER ... FOR ALL INSTANCES.
Static events
Section titled “Static events”Якщо EVENTS ... FOR ALL INSTANCES або CLASS-EVENTS — подія статична, підписка одна на весь клас. Використовуй для системних подій («будь-який order скасовано»), не для конкретного обʼєкта.
Ручний Observer без events
Section titled “Ручний Observer без events”Коли events незручні (треба програмно перебирати підписників, контролювати порядок), зроби реєстр руками:
CLASS zcl_subject DEFINITION. PUBLIC SECTION. METHODS: attach IMPORTING io_observer TYPE REF TO zif_observer, detach IMPORTING io_observer TYPE REF TO zif_observer, notify IMPORTING is_payload TYPE zty_payload. PRIVATE SECTION. DATA mt_observers TYPE STANDARD TABLE OF REF TO zif_observer WITH EMPTY KEY.ENDCLASS.
CLASS zcl_subject IMPLEMENTATION. METHOD notify. LOOP AT mt_observers INTO DATA(lo). lo->update( is_payload ). ENDLOOP. ENDMETHOD.ENDCLASS.Використовуй, коли треба:
- контролювати порядок сповіщення;
- фільтрувати по типу/предикату;
- зупиняти нотифікацію після першого успіху.
SAP-специфіка
Section titled “SAP-специфіка”cl_gui_alv_grid+ user-command events — канонічне місцеSET HANDLER.cl_gui_textedit,cl_gui_html_viewer— весь GUI-стек працює на events.cl_salv_events_table— події SALV (click, double_click, user_command) підписуються черезSET HANDLER.- Application Events у RAP — determination-и реагують на події CRUD.
- Business Events (tx SWEL, SWETYPV) — old-school, але досі зустрічається.
Підводні камені
Section titled “Підводні камені”- Витоки памʼяті. Якщо обʼєкт-subject тримає handler на обʼєкт-observer, а сам observer вже «вийшов зі сцени» — GC не прибере його, бо є зовнішнє посилання. У довгоживучих subject-ах явно
SET HANDLER ... ACTIVATION abap_falseабоDEACTIVATE EVENT. - Винятки у handler-і. Якщо один handler кинув exception, наступні handler-и можуть не виконатися — залежить від механізму (events у ABAP перериваються на першому unhandled).
- Синхронність. ABAP events — синхронні.
RAISE EVENTблокує subject, доки всі handler-и не відпрацюють. Для «послав і забув» — використовуй Update task (CALL FUNCTION ... IN UPDATE TASK) або фонове завдання. - Не ланцюжи події нескінченно. Якщо handler змінює subject, що знову кидає подію, яка знову тригерить handler — рекурсія. ABAP не захищає автоматично.
- Не плутай з publish-subscribe через message broker. ABAP events — in-process. Для міжсесійного messaging — qRFC/tRFC, BgRFC, Event Mesh.
Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Тільки один підписник, відомий статично — прямий виклик зрозуміліший.
- Підписка не змінюється —
IF/CASEз явним викликом тест-пригожіший і легше дебажити. - Потрібна гарантована послідовність і обробка помилок — події у ABAP дають мало контролю.