Шаблон посредника
В разработке программного обеспечения шаблон посредника определяет объект, который инкапсулирует взаимодействие набора объектов. Этот шаблон считается поведенческим шаблоном , поскольку он может изменить поведение программы при запуске.
В объектно-ориентированном программировании программы часто состоят из множества классов . Бизнес-логика и вычисления распределены между этими классами. Однако по мере добавления в программу большего количества классов, особенно во время обслуживания и/или рефакторинга , проблема взаимодействия между этими классами может стать более сложной. Это усложняет чтение и поддержку программы. Более того, изменить программу может оказаться затруднительно, поскольку любое изменение может повлиять на код в нескольких других классах.
С помощью шаблона посредника связь между объектами инкапсулируется внутри объекта-посредника . Объекты больше не взаимодействуют друг с другом напрямую, а общаются через посредника. Это уменьшает зависимости между взаимодействующими объектами, тем самым уменьшая связанность .
Обзор
[ редактировать ]Посредник [1] Шаблон проектирования — один из двадцати трех хорошо известных шаблонов проектирования , которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать.
Проблемы, которые может решить шаблон проектирования посредника [2]
[ редактировать ]- Следует избегать тесной связи между набором взаимодействующих объектов.
- Должна быть возможность самостоятельно изменять взаимодействие между набором объектов.
Определение набора взаимодействующих объектов путем прямого доступа и обновления друг друга негибко, поскольку оно тесно связывает объекты друг с другом и делает невозможным изменение взаимодействия независимо от объектов (без необходимости изменения). И это препятствует повторному использованию объектов и затрудняет их тестирование.
Тесно связанные объекты сложно реализовать, изменить, протестировать и повторно использовать, поскольку они ссылаются на множество разных объектов и знают о них.
Решения, описываемые шаблоном проектирования посредника
[ редактировать ]- Определите отдельный объект (посредник), который инкапсулирует взаимодействие между набором объектов.
- Объекты делегируют свое взаимодействие объекту-посреднику вместо того, чтобы взаимодействовать друг с другом напрямую.
Объекты взаимодействуют друг с другом косвенно через объект-посредник, который контролирует и координирует взаимодействие.
Это делает объекты слабо связанными . Они ссылаются только на свой объект-посредник и знают о нем и не имеют явных знаний друг о друге.
См. также класс UML и диаграмму последовательности ниже.
Определение
[ редактировать ]Суть шаблона посредника заключается в «определении объекта, который инкапсулирует взаимодействие набора объектов». Он способствует слабой связи, не позволяя объектам явно ссылаться друг на друга, и позволяет независимо изменять их взаимодействие. [3] [4] Клиентские классы могут использовать посредник для отправки сообщений другим клиентам и могут получать сообщения от других клиентов через событие в классе посредника.
Структура
[ редактировать ]Класс UML и диаграмма последовательности
[ редактировать ]
На приведенной выше UML классов диаграмме Colleague1
и Colleague2
классы не ссылаются (и не обновляют) друг на друга напрямую.
Вместо этого они относятся к общему Mediator
интерфейс для контроля и координации взаимодействия ( mediate()
), что делает их независимыми друг от друга в отношении способа осуществления взаимодействия.
Mediator1
класс реализует взаимодействие между Colleague1
и Colleague2
.
Диаграмма UML последовательности показывает взаимодействие во время выполнения. В этом примере Mediator1
объект
опосредует (контролирует и координирует) взаимодействие между Colleague1
и Colleague2
объекты.
Предполагая, что Colleague1
хочет взаимодействовать с Colleague2
(например, для обновления/синхронизации его состояния), Colleague1
звонки mediate(this)
на Mediator1
объект, который получает измененные данные из Colleague1
и выполняет action2()
на Colleague2
.
После этого
Colleague2
звонки mediate(this)
на Mediator1
объект, который получает измененные данные из Colleague2
и выполняет action1()
на Colleague1
.
Диаграмма классов
[ редактировать ]
- Участники
Медиатор — определяет интерфейс для связи между Коллеги объектами .
ConcreteMediator — реализует интерфейс посредника и координирует связь между объектами «Коллега» . Он осведомлен обо всех коллегах и их целях в отношении взаимодействия.
Коллега – определяет интерфейс для общения с другими Коллегами через своего Посредника.
ConcreteColleague — реализует интерфейс Коллеги и общается с другими Коллегами через своего Медиатора.
Пример
[ редактировать ]С#
[ редактировать ]Шаблон посредника гарантирует, что компоненты слабо связаны , так что они не вызывают друг друга явно, а вместо этого делают это посредством вызовов посредника. В следующем примере посредник регистрирует все компоненты, а затем вызывает их методы SetState.
interface IComponent
{
void SetState(object state);
}
class Component1 : IComponent
{
internal void SetState(object state)
{
throw new NotImplementedException();
}
}
class Component2 : IComponent
{
internal void SetState(object state)
{
throw new NotImplementedException();
}
}
// Mediates the common tasks
class Mediator
{
internal IComponent Component1 { get; set; }
internal IComponent Component2 { get; set; }
internal void ChangeState(object state)
{
this.Component1.SetState(state);
this.Component2.SetState(state);
}
}
Чат может использовать шаблон посредника или систему, в которой каждый из множества «клиентов» получает сообщение каждый раз, когда один из других клиентов выполняет действие (для чатов это будет когда каждый человек отправляет сообщение). В действительности использование шаблона посредника для чата будет практичным только при использовании удаленного взаимодействия . Использование необработанных сокетов не позволило бы использовать делегата обратные вызовы (люди подписались на событие MessageReceived класса Mediator).
public delegate void MessageReceivedEventHandler(string message, string sender);
public class Mediator
{
public event MessageReceivedEventHandler MessageReceived;
public void Send(string message, string sender)
{
if (MessageReceived != null)
{
Console.WriteLine("Sending '{0}' from {1}", message, sender);
MessageReceived(message, sender);
}
}
}
public class Person
{
private Mediator _mediator;
public string Name { get; set; }
public Person(Mediator mediator, string name)
{
Name = name;
_mediator = mediator;
_mediator.MessageReceived += new MessageReceivedEventHandler(Receive);
}
private void Receive(string message, string sender)
{
if (sender != Name)
Console.WriteLine("{0} received '{1}' from {2}", Name, message, sender);
}
public void Send(string message)
{
_mediator.Send(message, Name);
}
}
Ява
[ редактировать ]В следующем примере Mediator
объект управляет значениями нескольких Storage
объекты, заставляя пользовательский код получать доступ к сохраненным значениям через посредник. Когда объект хранения хочет выдать событие, указывающее, что его значение изменилось, он также возвращается к объекту-посреднику (через метод notifyObservers
), который управляет списком наблюдателей (реализован с использованием шаблона наблюдателя ).
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
class Storage<T> {
T value;
T getValue() {
return value;
}
void setValue(Mediator<T> mediator, String storageName, T value) {
this.value = value;
mediator.notifyObservers(storageName);
}
}
class Mediator<T> {
private final HashMap<String, Storage<T>> storageMap = new HashMap<>();
private final CopyOnWriteArrayList<Consumer<String>> observers = new CopyOnWriteArrayList<>();
public void setValue(String storageName, T value) {
Storage storage = storageMap.computeIfAbsent(storageName, name -> new Storage<>());
storage.setValue(this, storageName, value);
}
public Optional<T> getValue(String storageName) {
return Optional.ofNullable(storageMap.get(storageName)).map(Storage::getValue);
}
public void addObserver(String storageName, Runnable observer) {
observers.add(eventName -> {
if (eventName.equals(storageName)) {
observer.run();
}
});
}
void notifyObservers(String eventName) {
observers.forEach(observer -> observer.accept(eventName));
}
}
public class MediatorDemo {
public static void main(String[] args) {
Mediator<Integer> mediator = new Mediator<>();
mediator.setValue("bob", 20);
mediator.setValue("alice", 24);
mediator.getValue("alice").ifPresent(age -> System.out.println("age for alice: " + age));
mediator.addObserver("bob", () -> {
System.out.println("new age for bob: " + mediator.getValue("bob").orElseThrow(RuntimeException::new));
});
mediator.setValue("bob", 21);
}
}
См. также
[ редактировать ]- Посредничество данных
- «Шаблоны проектирования» — книга, положившая начало изучению шаблонов проектирования в информатике. [ нужна ссылка ]
- Шаблон проектирования программного обеспечения , стандартное решение распространенных проблем при проектировании программного обеспечения.
Ссылки
[ редактировать ]- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 273 и далее . ISBN 0-201-63361-2 .
{{cite book}}
: CS1 maint: несколько имен: список авторов ( ссылка ) - ^ Франке, Гюнтер. «Шаблон проектирования «Посредник» — проблема, решение и применимость» . w3sDesign . Проверено 12 августа 2017 г.
- ^ Гамма, Эрих ; Хелм, Ричард; Джонсон, Ральф ; Влиссидес, Джон (1994). Шаблоны проектирования . Аддисон-Уэсли . ISBN 0-201-63361-2 .
- ^ «Шаблон проектирования посредника» . Создание исходников .
- ^ Франке, Гюнтер. «Шаблон проектирования Посредник — структура и сотрудничество» . w3sDesign . Проверено 12 августа 2017 г.
Внешние ссылки
[ редактировать ]
- Кайзер, Бодо (21 сентября 2012 г.). «Рекомендуется ли использование шаблона-посредника?» . Переполнение стека .