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

Mediator

Mediator — поведінковий патерн: замість того, щоб обʼєкти знали один про одного і викликали один одного напряму, вони спілкуються через центрального посередника. Посередник інкапсулює правила взаємодії, компоненти стають незалежними, систему простіше міняти.

Диспетчерська вежа аеропорту. Пілоти не домовляються з пілотами інших літаків напряму — вони говорять з диспетчером. Диспетчер знає всіх і координує, хто коли злітає, сідає, куди рулює. Забери диспетчера — літаки не знатимуть, як взаємодіяти.

  • Система має багато компонентів, що тісно переплетені — кожен знає про кількох сусідів, зміна одного тягне правки всіх.
  • Треба централізувати складні правила взаємодії між UI-елементами (зміна одного поля вмикає/вимикає інші).
  • Хочеш тестувати компоненти ізольовано — напряму вони не знають один одного, тільки про mediator-інтерфейс.
  • Додавання нового компонента не повинно тягти правки існуючих.
" Контракт посередника
INTERFACE zif_mediator.
METHODS notify
IMPORTING io_sender TYPE REF TO object
iv_event TYPE string
is_data TYPE any OPTIONAL.
ENDINTERFACE.
" Базовий клас компонента — тримає ref на mediator
CLASS zcl_form_component DEFINITION PUBLIC ABSTRACT.
PUBLIC SECTION.
METHODS constructor IMPORTING io_mediator TYPE REF TO zif_mediator.
PROTECTED SECTION.
DATA mo_mediator TYPE REF TO zif_mediator.
ENDCLASS.
CLASS zcl_form_component IMPLEMENTATION.
METHOD constructor. mo_mediator = io_mediator. ENDMETHOD.
ENDCLASS.
" Конкретний компонент — поле вибору країни
CLASS zcl_field_country DEFINITION PUBLIC FINAL INHERITING FROM zcl_form_component.
PUBLIC SECTION.
METHODS set_value IMPORTING iv_country TYPE land1.
ENDCLASS.
CLASS zcl_field_country IMPLEMENTATION.
METHOD set_value.
mo_mediator->notify( io_sender = me
iv_event = 'COUNTRY_CHANGED'
is_data = iv_country ).
ENDMETHOD.
ENDCLASS.
" Конкретний посередник — правила координації
CLASS zcl_form_mediator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES zif_mediator.
METHODS:
register_country IMPORTING io TYPE REF TO zcl_field_country,
register_region IMPORTING io TYPE REF TO zcl_field_region,
register_city IMPORTING io TYPE REF TO zcl_field_city.
PRIVATE SECTION.
DATA:
mo_country TYPE REF TO zcl_field_country,
mo_region TYPE REF TO zcl_field_region,
mo_city TYPE REF TO zcl_field_city.
ENDCLASS.
CLASS zcl_form_mediator IMPLEMENTATION.
METHOD zif_mediator~notify.
CASE iv_event.
WHEN 'COUNTRY_CHANGED'.
mo_region->reset_list( is_data ).
mo_city->reset_list( ).
WHEN 'REGION_CHANGED'.
mo_city->reset_list( is_data ).
ENDCASE.
ENDMETHOD.
ENDCLASS.

Результат: поле «країна» не знає про поля «регіон» і «місто». Воно просто каже посереднику «країна змінилась» — правило «при зміні країни почистити регіон і місто» знаходиться в одному місці.

Схоже, але не те саме:

MediatorObserver
КаналиБагато-до-багатьох через центрОдин-до-багатьох, subject → observers
СемантикаКоординація (правила взаємодії)Сповіщення (подія → реакція)
ЗнанняMediator знає всіхSubject не знає, хто підписаний
ЗвʼязкиКомпоненти знають mediatorObserver знає subject, не навпаки

Можна комбінувати: mediator всередині використовує events для підписки, але для клієнтів — один вхід у notify( ).

  • cl_gui_cfw (Control Framework) — фактично mediator між ABAP-кодом та GUI-контролами. cl_gui_cfw=>dispatch( ), flush( ).
  • Business Communication Services (BCS) — mediator між вхідними email/fax/SMS і обробниками.
  • Controller у MVC — класичне місце для mediator: координує model і view, щоб вони не знали один про одного напряму.
  • Компоненти залежать від mediator. Гірше — усі залежать від одного типу mediator. Використовуй інтерфейс zif_mediator і передавай через конструктор (DI).
  • Цикли. Якщо mediator у відповідь на подію просить компонент щось зробити, а той знову повідомляє mediator — нескінченна рекурсія. Додавай прапорець «зараз у відповідь на подію X — не реагуй».
  • Тяжко дебажити. Потік керування непрямий: «хто кого викликав?» — треба читати mediator. Логуй виклики notify з sender-ом і event-ом.

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

Section titled “Коли НЕ використовувати”
  • Компонентів 2-3 — вони можуть знати один одного напряму.
  • Взаємодія проста — один метод у одному класі.
  • Немає ризику, що компонентів стане більше — не ускладнюй заздалегідь.