Книга доктора Яна Хонга «JAVA и шаблоны» начинается с описания шаблона «Посредник»:
Паттерн-посредник — это шаблон поведения объектов. Шаблон Mediator реализует способ взаимодействия набора объектов, поэтому им не нужно явно ссылаться друг на друга. Это позволяет им быть свободно связанными. Когда взаимодействия между некоторыми из этих объектов меняются, это не сразу влияет на взаимодействия между другими объектами. Это гарантирует, что эти взаимодействия могут изменяться независимо друг от друга.
Зачем нужен медиатор
Как показано на рисунке ниже, на этой схематической диаграмме имеется большое количество объектов. Эти объекты могут как влиять на другие объекты, так и подвергаться воздействию других объектов, поэтому их часто называют объектами-коллегами. Эти сотрудники формируют поведение системы посредством взаимодействия друг с другом. Как видно из рисунка, практически каждому объекту необходимо взаимодействовать с другими объектами, и это взаимодействие проявляется как непосредственная связь одного объекта с другим объектом. Это сверхсвязанная система.
Введя объект-посредник (Mediator), сетевую структуру системы можно преобразовать в звездообразную структуру с центром в медиаторе, как показано на рисунке ниже. В этой звездообразной структуре объект-коллега больше не взаимодействует с другим объектом посредством прямого соединения, а взаимодействует с другим объектом через объект-посредник; Существование объекта-посредника обеспечивает стабильность структуры объекта, то есть структура системы не будет вызывать больших модификаций из-за введения новых объектов.
Хороший объектно-ориентированный дизайн может улучшить сотрудничество и уменьшить связь между объектами. Хорошо продуманный проект разбивает систему на группу взаимодействующих объектов-коллег, затем наделяет каждый объект-коллегу уникальными обязанностями и соответствующим образом настраивает отношения сотрудничества между ними, чтобы они могли работать вместе.
Если нет материнской платы
Как мы все знаем, взаимодействие различных аксессуаров в компьютере в основном осуществляется через материнскую плату. Если в компьютере нет материнской платы, то различные аксессуары должны взаимодействовать друг с другом самостоятельно, чтобы передавать данные друг другу. А поскольку интерфейсы каждого аксессуара различны, при взаимодействии друг с другом интерфейс данных необходимо преобразовать для соответствия.
К счастью, взаимодействие различных аксессуаров полностью осуществляется через материнскую плату. Каждому аксессуару достаточно взаимодействовать только с материнской платой, а материнская плата знает, как обращаться со всеми аксессуарами, что значительно упрощает работу.
Структура паттерна посредника
Схематическая диаграмма классов для шаблона Mediator показана ниже:
Режим посредника включает в себя следующие роли:
● Роль абстрактного посредника (посредника). Определите интерфейс между объектом-коллегой и объектом-посредником, в котором основным методом является один (или несколько) методов событий.
● Роль конкретного посредника (ConcreteMediator): реализует метод события, объявленный абстрактным посредником. Конкретный посредник знает обо всех конкретных классах коллег и отвечает за конкретную координацию взаимодействия между каждым объектом коллеги.
● Роль абстрактного класса коллеги (Коллега): определяет интерфейс от посредника к объекту коллеги. Объекты-коллеги знают только о посреднике, а не о других коллегиальных объектах.
● Роль ConcreteColleague: все конкретные классы коллег наследуются от абстрактного класса коллег. Для реализации собственного бизнеса, когда вам необходимо общаться с другими коллегами, общайтесь с посредником, который его проводит. Посредник будет отвечать за взаимодействие с другими коллегами.
исходный код
Код копии абстрактного класса-посредника выглядит следующим образом:
Посредник публичного интерфейса {
/**
* Объект-коллега уведомляет метод-посредник при его изменении.
* Пусть посредник отвечает за соответствующие взаимодействия с другими объектами-коллегами.
*/
общественная пустота изменена (Коллега c);
}
конкретный класс медиатора
Скопируйте код кода следующим образом:
публичный класс ConcreteMediator реализует Mediator {
//Удерживать и поддерживать коллегу A
частный БетонКоллегаКоллегаА;
//Удерживать и поддерживать коллегу B
частный коллега ConcreteColleagueB;
public void setColleagueA (ConcreteColleagueA коллегиа) {
this.colleagueA = коллегаA;
}
общественный недействительный setColleagueB (ConcreteColleagueB коллегаB) {
this.colleagueB = коллегаB;
}
@Override
public void изменено(Коллега c) {
/**
* Определенный класс коллег изменился, что обычно требует взаимодействия с другими коллегами.
* Специально координируйте соответствующие объекты коллег для достижения совместного поведения.
*/
}
}
абстрактный класс коллеги
Скопируйте код кода следующим образом:
публичный абстрактный класс Коллега {
//Удерживаем объект-посредник
частный посредник; медиатор;
/**
*Конструктор
*/
общественный коллега(Медиатор-посредник){
this.mediator = посредник;
}
/**
* Получить объект-посредник, соответствующий текущему классу коллеги.
*/
публичный посредник getMediator() {
возвратный посредник;
}
}
Конкретный код для копирования того же класса выглядит следующим образом:
публичный класс ConcreteColleagueA расширяет коллегу {
public ConcreteColleagueA (посредник-посредник) {
супер(посредник);
}
/**
* Укажите методы выполнения тех или иных операций.
*/
публичная недействительная операция(){
//Уведомляем объект-посредник, когда вам нужно связаться с другими коллегами
getMediator().changed(это);
}
}
Скопируйте код кода следующим образом:
публичный класс ConcreteColleagueB расширяет коллегу {
public ConcreteColleagueB (посредник-посредник) {
супер(посредник);
}
/**
* Укажите методы выполнения тех или иных операций.
*/
публичная недействительная операция(){
//Уведомляем объект-посредник, когда вам нужно связаться с другими коллегами
getMediator().changed(это);
}
}
Используйте компьютер для просмотра фильмов
В повседневной жизни мы часто используем компьютеры для просмотра фильмов. Опишем этот процесс. После упрощения предположим, что будет следующий интерактивный процесс:
(1) Сначала оптический привод должен прочитать данные на оптическом диске, а затем сообщить материнской плате, что его статус изменился.
(2) Материнская плата получает данные с оптического привода и передает их в ЦП для анализа и обработки.
(3) После завершения обработки ЦП делит данные на видеоданные и аудиоданные и уведомляет материнскую плату о завершении обработки.
(4) Материнская плата получает данные, обработанные процессором, и передает их на видеокарту и звуковую карту соответственно для отображения видео и воспроизведения звука.
Чтобы реализовать пример с использованием шаблона посредника, необходимо различать объекты-коллеги и объекты-посредники. Очевидно, что материнская плата является посредником, а аксессуары, такие как оптические приводы, звуковые карты, процессоры и видеокарты, — их коллегами.
исходный код
Код копии абстрактного класса-коллеги выглядит следующим образом:
публичный абстрактный класс Коллега {
//Удерживаем объект-посредник
частный посредник; медиатор;
/**
*Конструктор
*/
общественный коллега(Медиатор-посредник){
this.mediator = посредник;
}
/**
* Получить объект-посредник, соответствующий текущему классу коллеги.
*/
публичный посредник getMediator() {
возвратный посредник;
}
}
Класс коллеги — код копирования оптического привода следующий:
публичный класс CDDriver расширяет коллегу {
//Данные считываются с оптического привода
частные строковые данные = "";
/**
*Конструктор
*/
общественный CDDriver (посредник-посредник) {
супер(посредник);
}
/**
* Получить данные, прочитанные с диска
*/
публичная строка getData() {
возврат данных;
}
/**
* Чтение компакт-диска
*/
общественная недействительность readCD () {
//Перед запятой данные, отображаемые в видео, а после запятой звук
this.data = "One Piece, я полон решимости стать королем пиратов";
//Сообщаем материнской плате, что ее статус изменился
getMediator().changed(это);
}
}
Класс коллеги — ЦП
Скопируйте код кода следующим образом:
процессор публичного класса расширяет коллегу {
//Разложенные видеоданные
частная строка videoData = "";
//Разложенные звуковые данные
частная строка soundData = "";
/**
*Конструктор
*/
общественный ЦП (посредник-посредник) {
супер(посредник);
}
/**
* Получить разложенные видеоданные
*/
публичная строка getVideoData() {
вернуть видеоданные;
}
/**
* Получить разложенные звуковые данные
*/
общественная строка getSoundData() {
вернуть звуковые данные;
}
/**
* Обработка данных и разделение данных на аудио и видео данные.
*/
общественная недействительность выполненияData (строковые данные) {
//Разложение данных: спереди — видеоданные, сзади — аудиоданные.
String[] array = data.split(",");
this.videoData = массив [0];
this.soundData = массив [1];
//Сообщаем материнской плате, что ЦП завершил работу
getMediator().changed(это);
}
}
Класс коллеги — код копирования кода видеокарты следующий:
публичный класс VideoCard расширяет коллегу {
/**
*Конструктор
*/
общественная VideoCard (посредник-посредник) {
супер(посредник);
}
/**
* Отображение видеоданных
*/
общественная недействительность showData (строковые данные) {
System.out.println("Вы смотрите: " + данные);
}
}
Коллега по классу - код копирования звуковой карты следующий:
публичный класс SoundCard расширяет коллегу {
/**
*Конструктор
*/
общественная SoundCard (посредник-посредник) {
супер(посредник);
}
/**
* Создание звуков на основе аудиоданных
*/
public void soundData (строковые данные) {
System.out.println("Озвучка: " + данные);
}
}
Код копии абстрактного класса-посредника выглядит следующим образом:
Посредник публичного интерфейса {
/**
* Объект-коллега уведомляет метод-посредник при его изменении.
* Пусть посредник отвечает за соответствующие взаимодействия с другими объектами-коллегами.
*/
общественная пустота изменена (Коллега c);
}
Конкретный код копирования класса посредника выглядит следующим образом:
публичный класс MainBoard реализует Mediator {
//Необходимо знать класс коллеги для взаимодействия — класс оптического привода
частный CDDriver cdDriver = null;
//Необходимо знать класс коллеги для взаимодействия с классом ЦП
частный процессор процессора = ноль;
//Необходимо знать класс коллег для взаимодействия - класс видеокарты
частная видеокарта videoCard = null;
//Необходимо знать класс коллег для взаимодействия - класс звуковой карты
частная звуковая карта soundCard = null;
общественный недействительный setCdDriver (CDDriver cdDriver) {
this.cdDriver = cdDriver;
}
public void setCpu (ЦП) {
this.cpu = процессор;
}
public void setVideoCard(VideoCard videoCard) {
this.videoCard = видеокарта;
}
public void setSoundCard (SoundCard soundCard) {
this.soundCard = soundCard;
}
@Override
public void изменено(Коллега c) {
если (c экземпляр CDDriver) {
//Указывает, что оптический привод прочитал данные
this.opeCDDriverReadData((CDDriver)c);
}иначе если(c экземпляр ЦП){
this.opeCPU((ЦП)c);
}
}
/**
* Обработка взаимодействия с другими объектами после считывания данных оптическим приводом.
*/
частная пустота opeCDDriverReadData (CDDriver cd) {
//Сначала получаем данные, считанные оптическим приводом
Строковые данные = cd.getData();
//Передаем эти данные в ЦП для обработки
процессор.executeData(данные);
}
/**
* Обработка взаимодействия между ЦП и другими объектами после обработки данных
*/
частная пустота opeCPU (ЦП) {
//Сначала получаем данные, обработанные процессором
Строка videoData = cpu.getVideoData();
Строка soundData = cpu.getSoundData();
//Переносим эти данные на видеокарту и звуковую карту для отображения
videoCard.showData(видеоДанные);
soundCard.soundData(soundData);
}
}
Код копирования клиентского класса выглядит следующим образом:
Клиент публичного класса {
public static void main(String[] args) {
//Создаем посредник - материнскую плату
Медиатор MainBoard = новый MainBoard();
//Создаем класс коллеги
CDDriver cd = новый CDDriver(посредник);
ЦП ЦП = новый ЦП (посредник);
VideoCard vc = новая VideoCard(посредник);
SoundCard sc = новая SoundCard (посредник);
//Сообщаем посреднику всех коллег
mediator.setCdDriver(cd);
медиатор.setCpu(процессор);
mediator.setVideoCard(ВК);
mediator.setSoundCard(sc);
//Начинаем просмотр фильма, вставляем диск в оптический привод и оптический привод начинает читать диск
cd.readCD();
}
}
Результаты бега следующие:
Преимущества шаблона посредника
● слабая связь
Шаблон посредника инкапсулирует взаимодействие между несколькими объектами-коллегами в объект-посредник, тем самым слабо связывая объекты-коллеги и, по сути, достигая взаимодополняющих зависимостей. Таким образом, коллегиальные объекты можно будет изменять и повторно использовать независимо, вместо того, чтобы «перемещать одно место и затрагивать все тело», как раньше.
● Централизованный контроль взаимодействий
Взаимодействия нескольких объектов-коллег инкапсулируются в объекте-посреднике для централизованного управления, поэтому при изменении этого интерактивного поведения вам нужно только изменить объект-посредник. Конечно, если это уже завершенная система, то разверните объект-посредник. и каждый класс коллег не нужно изменять.
● Схема «многие-ко-многим» становится «один-ко-многим».
Когда шаблон посредника не используется, связь между объектами-коллегами обычно является связью «многие-ко-многим». После введения объекта-посредника связь между объектом-посредником и объектом-коллегой обычно становится двусторонней: «один-ко-многим». что упрощает понимание и реализацию связей между объектами.
Недостатки шаблона посредника
Одним из потенциальных недостатков модели посредника является чрезмерная централизация. Если взаимодействие объектов-коллег очень велико и сложно, когда все эти сложности сосредоточены на посреднике, объект-посредник станет очень сложным и трудным в управлении и сопровождении.