Mediator (Посредник)¶
Категория: паттерн поведения.
Проблема¶
Когда объекты общаются друг с другом напрямую, граф связей между ними быстро превращается в "все со всеми": чекбокс знает про кнопку, кнопка знает про поле ввода, поле ввода знает про чекбокс и так далее. Такой код сложно менять - изменение одного компонента требует понимания всех, с кем он напрямую связан, а количество связей растёт нелинейно с числом компонентов.
Решение¶
- Вводится отдельный объект - посредник, который знает обо всех участвующих компонентах.
- Компоненты перестают ссылаться друг на друга напрямую - вместо этого они сообщают посреднику о своих изменениях (
mediator.Notify(this, "событие")). - Вся логика согласованного взаимодействия компонентов ("если чекбокс не отмечен - кнопка недоступна") переносится в посредника.
- В результате получается связность "звездой": каждый компонент связан только с посредником, а не друг с другом.
Структура¶
Mediator- интерфейс посредника с методом уведомления о событиях.ConcreteMediator- хранит ссылки на все компоненты и реализует логику их согласования.Colleague(компоненты) - хранят ссылку только на посредника, уведомляют его о своих изменениях и получают от него команды.
Явный и неявный посредник¶
- Явный посредник - отдельный класс, как в примере выше (
CheckoutDialog). Логика взаимодействия сосредоточена в одном месте и её легко найти. - Неявный посредник - роль медиатора распределена между самими компонентами (например, через события/подписки без единого координирующего класса). Такой вариант проще для маленького числа связей, но с ростом системы логика координации "размазывается" и её сложнее отследить.
Когда применять¶
- Много компонентов с сильными, запутанными взаимными связями, где изменение одного требует правки нескольких других.
- Логика взаимодействия компонентов регулярно меняется, и хочется менять её в одном месте, не трогая сами компоненты.
- Компоненты должны быть переиспользуемы в разных контекстах (разные экраны, разные диалоги) с разной логикой согласования между ними.
Плюсы¶
- Уменьшает связанность между компонентами - каждый знает только про посредника.
- Логика взаимодействия сосредоточена в одном месте, что упрощает её изменение и тестирование.
- Компоненты становится проще переиспользовать отдельно друг от друга.
Минусы¶
- Посредник рискует превратиться в "божественный объект" (God Object), который знает слишком много и становится узким местом при сопровождении.
- Добавляет косвенность: чтобы понять, что произойдёт при изменении одного компонента, нужно смотреть в код посредника, а не в код самого компонента.
Когда третий лишний¶
Не любое взаимодействие двух объектов нуждается в посреднике. Если связь между двумя конкретными классами простая, стабильная и не разрастается, введение отдельного медиатора добавляет уровень косвенности без реальной выгоды - в таком случае прямая связь или обычное событие/делегат проще и понятнее.
Пример в .NET Framework / BCL¶
- Классы-контроллеры/координаторы в UI-фреймворках (WPF
ViewModel, который координирует несколькоCommandи свойств, не позволяя элементам управления знать друг о друге). - Библиотека MediatR (сторонняя, но фактически имя говорит само за себя) - реализует паттерн Mediator для развязывания отправителей и обработчиков запросов/событий в приложении.
- Оркестраторы бизнес-процессов, координирующие вызовы нескольких сервисов, которые сами ничего не знают друг о друге.
Пример реализации на C#¶
| Mediator.cs | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | |