В программной инженерии шаблон посредника определяет объект, который инкапсулирует способ взаимодействия набора объектов. Этот шаблон считается поведенческим шаблоном из-за того, как он может изменять поведение программы во время ее работы.
В объектно-ориентированном программировании программы часто состоят из множества классов . Бизнес-логика и вычисления распределены между этими классами. Однако по мере добавления большего количества классов в программу, особенно во время обслуживания и/или рефакторинга , проблема связи между этими классами может стать более сложной. Это затрудняет чтение и поддержку программы. Кроме того, может стать трудно изменять программу, поскольку любое изменение может повлиять на код в нескольких других классах.
С шаблоном посредника коммуникация между объектами инкапсулируется в объекте-посреднике . Объекты больше не общаются друг с другом напрямую, а вместо этого общаются через посредника. Это уменьшает зависимости между общающимися объектами, тем самым уменьшая связанность .
Шаблон проектирования «Посредник » [1] — один из двадцати трех известных шаблонов проектирования , описывающих, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые проще реализовывать, изменять, тестировать и повторно использовать.
Определение набора взаимодействующих объектов путем прямого доступа и обновления друг друга негибко, поскольку это тесно связывает объекты друг с другом и делает невозможным изменение взаимодействия независимо от (без необходимости изменения) объектов. И это делает объекты невозможными для повторного использования и затрудняет их тестирование.
Тесно связанные объекты трудно реализовывать, изменять, тестировать и повторно использовать, поскольку они ссылаются на множество различных объектов и знают о них.
Объекты взаимодействуют друг с другом косвенно через объект-посредник, который контролирует и координирует взаимодействие.
Это делает объекты слабосвязанными . Они ссылаются только на свой объект-посредник и знают о нем, но не имеют явных знаний друг о друге.
См. также класс UML и диаграмму последовательности ниже.
Суть шаблона посредника заключается в «определении объекта, который инкапсулирует способ взаимодействия набора объектов». Он способствует слабой связанности, не давая объектам явно ссылаться друг на друга, и позволяет изменять их взаимодействие независимо. [3] [4] Клиентские классы могут использовать посредника для отправки сообщений другим клиентам и могут получать сообщения от других клиентов через событие в классе посредника.
В приведенной выше диаграмме классов UML классы и не ссылаются друг на друга (и не обновляют) напрямую. Вместо этого они ссылаются на общий интерфейс для управления и координации взаимодействия ( ), что делает их независимыми друг от друга относительно того, как осуществляется взаимодействие. Класс реализует взаимодействие между и .Colleague1
Colleague2
Mediator
mediate()
Mediator1
Colleague1
Colleague2
Диаграмма последовательности UML показывает взаимодействия во время выполнения. В этом примере объект опосредует (управляет и координирует) взаимодействие между и объектами.Mediator1
Colleague1
Colleague2
Предполагая, что Colleague1
требуется взаимодействовать с Colleague2
(например, обновить/синхронизировать его состояние), Colleague1
вызывается mediate(this)
объект Mediator1
, который получает измененные данные Colleague1
и выполняет action2()
on Colleague2
.
После этого Colleague2
вызывается mediate(this)
объект Mediator1
, который получает измененные данные Colleague2
и выполняет action1()
on Colleague1
.
Посредник — определяет интерфейс для связи между объектами Colleague .
ConcreteMediator - реализует интерфейс посредника и координирует коммуникацию между объектами Colleague . Он знает обо всех Colleague и их целях в отношении межкоммуникации.
Коллега - определяет интерфейс для общения с другими Коллегами через своего Посредника.
ConcreteColleague — реализует интерфейс Colleague и общается с другими Colleague через своего посредника.
Шаблон посредника обеспечивает слабую связанность компонентов , так что они не вызывают друг друга явно, а вместо этого делают это через вызовы посредника. В следующем примере посредник регистрирует все компоненты, а затем вызывает их методы SetState.
интерфейс IComponent { void SetState ( состояние объекта ); } класс Component1 : IComponent { внутренний void SetState ( состояние объекта ) { throw new NotImplementedException (); } } класс Component2 : IComponent { внутренний void SetState ( состояние объекта ) { throw new NotImplementedException (); } } // Выполняет общие задачи class Mediator { internal IComponent Component1 { get ; set ; } internal IComponent Component2 { get ; set ; } внутренний void ChangeState ( состояние объекта ) { этот.Компонент1.УстановитьСостояние ( состояние ) ; этот.Компонент2.УстановитьСостояние ( состояние ) ; } }
Чат-комната может использовать шаблон посредника или систему, в которой множество «клиентов» получают сообщение каждый раз, когда один из других клиентов выполняет действие (для чат-комнат это будет, когда каждый человек отправляет сообщение). В действительности использование шаблона посредника для чат-комнаты будет практичным только при использовании с удаленным доступом . Использование сырых сокетов не позволит выполнять обратные вызовы делегата (люди подписались на событие MessageReceived класса Mediator).
открытый делегат 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 ( " Отправка '{0}' от {1}" , message , sender ) ; MessageReceived ( message , sender ); } } } публичный класс Персона { частный Посредник _медиатор ; Имя публичной строки { получить ; установить ; } 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} получил '{1}' от {2}" , Name , message , sender ) ; } public void Send ( string message ) { _mediator.Send ( message , Name ) ; } }
В следующем примере Mediator
объект управляет значениями нескольких Storage
объектов, заставляя пользовательский код получать доступ к сохраненным значениям через посредника. Когда объект хранения хочет выдать событие, указывающее на то, что его значение изменилось, он также возвращается к объекту-посреднику (через метод notifyObservers
), который управляет списком наблюдателей (реализовано с использованием шаблона наблюдателя ).
импорт java.util.HashMap ; импорт java.util.Optional ; импорт java.util.concurrent.CopyOnWriteArrayList ; импорт 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 ( storageName.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 ( " боб " , 20 ); mediator.setValue ( " элиса " , 24 ); mediator.getValue ( "элиса" ) .ifPresent ( age -> System.out.println ( " возраст для элис: " + age ) ); mediator.addObserver ( " боб " , ( ) - > { System.out.println ( " новый возраст для боба : " + mediator.getValue ( " боб " ) .orElseThrow ( RuntimeException :: new )); }); mediator.setValue ( " боб " , 21 ) ; } }
{{cite book}}
: CS1 maint: несколько имен: список авторов ( ссылка )