Chain of Responsibility
Chain of Responsibility (CoR) — поведінковий патерн: запит передається по ланцюжку обробників, поки один з них не візьметься його обробити. Клієнт не знає, хто саме обробить запит — він просто передає його першому у ланцюгу.
Метафора
Section titled “Метафора”Звернення у службу підтримки: спочатку відповідає бот, якщо не допоміг — пересилає оператору першої лінії, той — другій, друга — інженеру. На кожному рівні: «ти вирішуєш це? Ні? — передаю далі». Клієнт дзвонить один раз — решта відбувається без нього.
Коли застосовувати
Section titled “Коли застосовувати”- Обробники можуть змінюватись — треба додавати/прибирати рівні без правки клієнта.
- Порядок обробки важливий, і має бути явним (сконфігурованим).
- Запит не завжди має обробника — дозволено пройти ланцюг наскрізь.
- Треба паралельно логувати/валідовувати/авторизовувати запит на кількох рівнях.
ABAP-реалізація
Section titled “ABAP-реалізація”CLASS zcl_approval_handler DEFINITION PUBLIC ABSTRACT. PUBLIC SECTION. METHODS: set_next IMPORTING io_next TYPE REF TO zcl_approval_handler RETURNING VALUE(ro) TYPE REF TO zcl_approval_handler, handle IMPORTING iv_amount TYPE p RETURNING VALUE(rv_approved) TYPE abap_bool.
PROTECTED SECTION. METHODS can_handle ABSTRACT IMPORTING iv_amount TYPE p RETURNING VALUE(rv_can) TYPE abap_bool. METHODS approve ABSTRACT IMPORTING iv_amount TYPE p.
DATA mo_next TYPE REF TO zcl_approval_handler.ENDCLASS.
CLASS zcl_approval_handler IMPLEMENTATION. METHOD set_next. mo_next = io_next. ro = io_next. " повертаємо next, щоб можна було чейнити ENDMETHOD.
METHOD handle. IF can_handle( iv_amount ). approve( iv_amount ). rv_approved = abap_true. ELSEIF mo_next IS BOUND. rv_approved = mo_next->handle( iv_amount ). ELSE. rv_approved = abap_false. " нікому не по зубах ENDIF. ENDMETHOD.ENDCLASS.
" Конкретні рівніCLASS zcl_approver_team_lead DEFINITION PUBLIC FINAL INHERITING FROM zcl_approval_handler. PROTECTED SECTION. METHODS: can_handle REDEFINITION, approve REDEFINITION.ENDCLASS.
CLASS zcl_approver_team_lead IMPLEMENTATION. METHOD can_handle. rv_can = COND #( WHEN iv_amount <= 1000 THEN abap_true ). ENDMETHOD. METHOD approve. " лог апруву ... ENDMETHOD.ENDCLASS.
CLASS zcl_approver_director DEFINITION PUBLIC FINAL INHERITING FROM zcl_approval_handler. PROTECTED SECTION. METHODS: can_handle REDEFINITION, approve REDEFINITION.ENDCLASS.
CLASS zcl_approver_director IMPLEMENTATION. METHOD can_handle. rv_can = COND #( WHEN iv_amount <= 10000 THEN abap_true ). ENDMETHOD. METHOD approve. " ... ENDMETHOD.ENDCLASS.
CLASS zcl_approver_ceo DEFINITION PUBLIC FINAL INHERITING FROM zcl_approval_handler. PROTECTED SECTION. METHODS: can_handle REDEFINITION, approve REDEFINITION.ENDCLASS.
CLASS zcl_approver_ceo IMPLEMENTATION. METHOD can_handle. rv_can = abap_true. ENDMETHOD. " CEO апрувить усе METHOD approve. " ... ENDMETHOD.ENDCLASS.Використання:
DATA(lo_tl) = NEW zcl_approver_team_lead( ).DATA(lo_dir) = NEW zcl_approver_director( ).DATA(lo_ceo) = NEW zcl_approver_ceo( ).
lo_tl->set_next( lo_dir )->set_next( lo_ceo ).
DATA(lv_ok) = lo_tl->handle( iv_amount = 7500 )." 7500 > 1000 → team lead передає → director: 7500 <= 10000 → apruvитьВаріації
Section titled “Варіації”- Broadcast chain. Кожен обробник робить свою роботу і передає далі (наприклад, логер → аудитор → метрики — всі мають обробити подію).
- Short-circuit. Як у прикладі вище — перший, хто може, обробляє і ланцюг зупиняється.
- Chain + Command. Запит — обʼєкт-Command, ланцюг вирішує, хто його виконає.
SAP-специфіка
Section titled “SAP-специфіка”- Event filters у BAdI — серія фільтрів, що по черзі вирішують, чи реагувати на подію.
CL_SHDB_SELTAB+ filter chain у деяких фреймворках перевірки повноважень.- ALE IDoc inbound — ланцюг
INBOUND_FUNCTION_MODULE→IDoc_Input_*— якщо немає FM для цього типу, пробується fallback. - Exception handling з кількома
CATCH— не зовсім CoR, але концептуально схоже: виняток іде по стеку, поки хтось не обробить.
Підводні камені
Section titled “Підводні камені”- Запит загубився. Якщо жоден обробник не взяв — з клієнта повернеться
abap_false/NULL, і клієнт має це обробити. Не забудь явний «catch-all» на кінці. - Порядок у ланцюгу. Порядок — частина логіки. Збирай ланцюг в одному місці (фабрика chain-у), не розкидуй
set_nextпо коду. - Витоки у циклічних ланцюгах. Якщо випадково
A → B → A— нескінченна рекурсія. Обʼєднуй ланцюг унікальними посиланнями, перевіряй коло. - Важко простежити. Якщо 10 обробників, і запит іде через п’ятого — дебаг тяжкий. Логуй вхід/вихід кожного handler-а з ідентифікатором запиту.
Коли НЕ використовувати
Section titled “Коли НЕ використовувати”- Відома наперед послідовність дій — просто пиши її.
- Обробників 2-3 —
IF/ELSEзрозуміліший. - Треба обовʼязково знати, хто обробив — CoR ховає це; краще явна фабрика обробника.