Назначение
Из группы сложно взаимосвязанных классов предлагает выделить отдельный класс-медиатор, который будет отслеживать взаимное изменение состояний и инкапсулировать код, обрабатывающий взаимодействия, до этого происходящие напрямую.
- позволяет переиспользовать классы, которые раньше были сильно связаны между собой;
- позволяет легче изменять классы, так как у них меньше зависимостей;
- избавляет от необходимости создавать тонны подклассов чтобы просто переиспользовать их в немного другом контексте.
Описание
Допустим, имеется система, состоящая из множества взаимосвязанных компонентов. Это удобно представить на графическом интерфейсе с элементами управления: TextInput, CheckBox, Button, DateInput. Состояние некоторых элементов влияет на состояние других элементов: элемент может быть заблокирован, до тех пор пока другой элемент не получит валидное значение.
Наивный подход состоит в непосредственном связывании зависимых элементов напрямую и обмен состоянием между ними. Этот подход плох тем, что между компонентами системы появляются сильные связи, их становится сложно переиспользовать и изменять.
Для того чтобы решить проблему сильной взаимной связи между всеми компонентами, предлагается ввести класс, ответственный за управление инвариантами состояния. Этот класс-mediator взаимодействует с каждым компонентом через механизм событий: компоненты являются источниками событий, а mediator - получателем. Mediator обеспечивает переход зависимых компонентов при изменении состояния компонентов, от которых они зависят.
Реализация
Для каждого события определяется интерфейс слушателя события этого типа. Компоненты генерируют события некоторого типа и через интерфейс уведомляют об этом всех зарегистрированных слушателей. В качестве слушателей выступает Mediator - специальный класс, ответственный за координацию взаимодействия между компонентами. Mediator либо реализует интерфейсы слушателя различных событий, либо устанавливает callback’и при регистрации компонентов. Callback’и являются анонимными внутренними классами и имеют доступ к состоянию Mediator. Также mediator хранит ссылки на все компоненты и вызывает public методы компонентов для поддержания инвариантов. Например, он активирует/декативирует компоненты в зависимости от событий, сгенерированных другими компонентами.
Примеры
Допустим, есть диалог, в состав которого входит текстовое поля для ввода имени, кнопка “Ok” и другие элементы. В зависимости от состояния текстового поля кнопка должна быть активна или деактивирована. При действии на некоторых других компонентах значение текстового поля должно сбрасываться или устанавливаться. Для взаимного управления состоянием вводится класс Mediator. Этот класс способен регистрировать в себе компоненты диалога. При регистрации компонентов, Mediator сохраняет ссылку на компонент и сам регистрируется в компоненте как слушатель, для того чтобы получать уведомления о смене состояния. Методы в Mediator соответствуют событиям в компонентах и управляют взаимным влиянием состояний компонентов.
Варианты
- Mediator может либо реализовывать интерфейсы слушателя событий, которые генерируют компоненты, либо объявлять собственный интерфейс (см. refactoring.guru). Методы собственного интерфейса Mediator должны вызваться самими компонентами. То есть во втором случае, компоненты должны хранить ссылку на интерфейс модератора. В первом случае Mediator получает информацию о том, какой объект вызвал событие через замыкание в callback’е. Во втором случае интерфейс должен предусматривать передачу объекта, вызвавшего событие через аргументы методов интерфейса.
- Роль Mediator может естественным образом играть контейнер всех компонентов, например, сам диалог. А может быть введен отдельно искусственный объект.
- Есть вариант реализации, при котором Mediator сам поддерживает актуальную информацию о состоянии всех связанных компонентов внутри себя. А есть вариант, когда Mediator для получения информации о состоянии каждый раз обращается к компоненту. Второй вариант менее подвержен ошибкам.
Чем отличается
God object может быть со временем стать следствием реализации шаблона Mediator.
Observer часто используется как часть Mediator, для того чтобы зарегистрировать Mediator в качестве слушателя событий компонентов.
Ссылки
https://java-design-patterns.com/patterns/mediator/
https://github.com/iluwatar/java-design-patterns/tree/master/mediator