Перейти до вмісту

Chain of Responsibility

Chain of Responsibility (CoR) — поведінковий патерн: запит передається по ланцюжку обробників, поки один з них не візьметься його обробити. Клієнт не знає, хто саме обробить запит — він просто передає його першому у ланцюгу.

Звернення у службу підтримки: спочатку відповідає бот, якщо не допоміг — пересилає оператору першої лінії, той — другій, друга — інженеру. На кожному рівні: «ти вирішуєш це? Ні? — передаю далі». Клієнт дзвонить один раз — решта відбувається без нього.

  • Обробники можуть змінюватись — треба додавати/прибирати рівні без правки клієнта.
  • Порядок обробки важливий, і має бути явним (сконфігурованим).
  • Запит не завжди має обробника — дозволено пройти ланцюг наскрізь.
  • Треба паралельно логувати/валідовувати/авторизовувати запит на кількох рівнях.
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ить
  • Broadcast chain. Кожен обробник робить свою роботу і передає далі (наприклад, логер → аудитор → метрики — всі мають обробити подію).
  • Short-circuit. Як у прикладі вище — перший, хто може, обробляє і ланцюг зупиняється.
  • Chain + Command. Запит — обʼєкт-Command, ланцюг вирішує, хто його виконає.
  • Event filters у BAdI — серія фільтрів, що по черзі вирішують, чи реагувати на подію.
  • CL_SHDB_SELTAB + filter chain у деяких фреймворках перевірки повноважень.
  • ALE IDoc inbound — ланцюг INBOUND_FUNCTION_MODULEIDoc_Input_* — якщо немає FM для цього типу, пробується fallback.
  • Exception handling з кількома CATCH — не зовсім CoR, але концептуально схоже: виняток іде по стеку, поки хтось не обробить.
  • Запит загубився. Якщо жоден обробник не взяв — з клієнта повернеться abap_false/NULL, і клієнт має це обробити. Не забудь явний «catch-all» на кінці.
  • Порядок у ланцюгу. Порядок — частина логіки. Збирай ланцюг в одному місці (фабрика chain-у), не розкидуй set_next по коду.
  • Витоки у циклічних ланцюгах. Якщо випадково A → B → A — нескінченна рекурсія. Обʼєднуй ланцюг унікальними посиланнями, перевіряй коло.
  • Важко простежити. Якщо 10 обробників, і запит іде через п’ятого — дебаг тяжкий. Логуй вхід/вихід кожного handler-а з ідентифікатором запиту.

Коли НЕ використовувати

Section titled “Коли НЕ використовувати”
  • Відома наперед послідовність дій — просто пиши її.
  • Обробників 2-3 — IF/ELSE зрозуміліший.
  • Треба обовʼязково знати, хто обробив — CoR ховає це; краще явна фабрика обробника.