Паттерн Посредник (Mediator) представляет такой шаблон проектирования, который обеспечивает взаимодействие множества объектов без необходимости ссылаться друг на друга. Тем самым достигается слабосвязанность взаимодействующих объектов.
Когда используется паттерн Посредник?
- Когда имеется множество взаимосвязаных объектов, связи между которыми сложны и запутаны.
- Когда необходимо повторно использовать объект, однако повторное использование затруднено в силу сильных связей с другими объектами.
Схематично с помощью UML паттерн можно описать следующим образом:
Формальная структура классов и связей между ними с применением паттерна на языке C#:
abstract class Mediator
{
public abstract void Send(string msg, Colleague colleague);
}
abstract class Colleague
{
protected Mediator mediator;
public Colleague(Mediator mediator)
{
this.mediator = mediator;
}
}
class ConcreteColleague1 : Colleague
{
public ConcreteColleague1(Mediator mediator)
: base(mediator)
{ }
public void Send(string message)
{
mediator.Send(message, this);
}
public void Notify(string message)
{ }
}
class ConcreteColleague2 : Colleague
{
public ConcreteColleague2(Mediator mediator)
: base(mediator)
{ }
public void Send(string message)
{
mediator.Send(message, this);
}
public void Notify(string message)
{ }
}
class ConcreteMediator : Mediator
{
public ConcreteColleague1 Colleague1 { get; set; }
public ConcreteColleague2 Colleague2 { get; set; }
public override void Send(string msg, Colleague colleague)
{
if (Colleague1 == colleague)
Colleague2.Notify(msg);
else
Colleague1.Notify(msg);
}
}
Участники
- Mediator: представляет интерфейс для взаимодействия с объектами Colleague
- Colleague: представляет интерфейс для взаимодействия с объектом Mediator
- ConcreteColleague1 и ConcreteColleague2: конкретные классы коллег, которые обмениваются друг с другом через объект Mediator
- ConcreteMediator: конкретный посредник, реализующий интерфейс типа Mediator
Рассмотрим реальный пример. Система создания программных продуктов включает ряд акторов: заказчики, программисты, тестировщики и так далее. Но нередко все эти акторы взаимодействуют между собой не непосредственно, а опосредованно через менеджера проектов. То есть менеджер проектов выполняет роль посредника. В этом случае процесс взаимодействия между объектами мы могли бы описать так:
class Program
{
static void Main(string[] args)
{
ManagerMediator mediator = new ManagerMediator();
Colleague customer = new CustomerColleague(mediator);
Colleague programmer = new ProgrammerColleague(mediator);
Colleague tester = new TesterColleague(mediator);
mediator.Customer = customer;
mediator.Programmer = programmer;
mediator.Tester = tester;
customer.Send("Есть заказ, надо сделать программу");
programmer.Send("Программа готова, надо протестировать");
tester.Send("Программа протестирована и готова к продаже");
Console.Read();
}
}
abstract class Mediator
{
public abstract void Send(string msg, Colleague colleague);
}
abstract class Colleague
{
protected Mediator mediator;
public Colleague(Mediator mediator)
{
this.mediator = mediator;
}
public virtual void Send(string message)
{
mediator.Send(message, this);
}
public abstract void Notify(string message);
}
// класс заказчика
class CustomerColleague : Colleague
{
public CustomerColleague(Mediator mediator)
: base(mediator)
{ }
public override void Notify(string message)
{
Console.WriteLine("Сообщение заказчику: " + message);
}
}
// класс программиста
class ProgrammerColleague : Colleague
{
public ProgrammerColleague(Mediator mediator)
: base(mediator)
{ }
public override void Notify(string message)
{
Console.WriteLine("Сообщение программисту: " + message);
}
}
// класс тестера
class TesterColleague : Colleague
{
public TesterColleague(Mediator mediator)
: base(mediator)
{ }
public override void Notify(string message)
{
Console.WriteLine("Сообщение тестеру: " + message);
}
}
class ManagerMediator : Mediator
{
public Colleague Customer { get; set; }
public Colleague Programmer { get; set; }
public Colleague Tester { get; set; }
public override void Send(string msg, Colleague colleague)
{
// если отправитель - заказчик, значит есть новый заказ
// отправляем сообщение программисту - выполнить заказ
if (Customer == colleague)
Programmer.Notify(msg);
// если отправитель - программист, то можно приступать к тестированию
// отправляем сообщение тестеру
else if (Programmer == colleague)
Tester.Notify(msg);
// если отправитель - тест, значит продукт готов
// отправляем сообщение заказчику
else if (Tester == colleague)
Customer.Notify(msg);
}
}
Класс менеджера — ManagerMediator в методе Send()
проверяет, от кого пришло сообщение, и в зависимости от отправителя перенаправляет его другому объекту с помощью методов Notify()
, определенных в классе Colleague.
Консольный вывод программы:
Сообщение программисту: Есть заказ, надо сделать программу Сообщение тестеру: Программа готова, надо протестировать Сообщение заказчику: Программа протестирована и готова к продаже
В итоге применение паттерна Посредник дает нам следующие преимущества:
- Устраняется сильная связанность между объектами Colleague
- Упрощается взаимодействие между объектами: вместо связей по типу «все-ко-всем» применяется связь «один-ко-всем»
- Взаимодействие между объектами абстрагируется и выносится в отдельный интерфейс
- Централизуется управления отношениями между объектами
еще один пример:
using System;
namespace RefactoringGuru.DesignPatterns.Mediator.Conceptual
{
// Интерфейс Посредника предоставляет метод, используемый компонентами для
// уведомления посредника о различных событиях. Посредник может реагировать
// на эти события и передавать исполнение другим компонентам.
public interface IMediator
{
void Notify(object sender, string ev);
}
// Конкретные Посредники реализуют совместное поведение, координируя
// отдельные компоненты.
class ConcreteMediator : IMediator
{
private Component1 _component1;
private Component2 _component2;
public ConcreteMediator(Component1 component1, Component2 component2)
{
this._component1 = component1;
this._component1.SetMediator(this);
this._component2 = component2;
this._component2.SetMediator(this);
}
public void Notify(object sender, string ev)
{
if (ev == "A")
{
Console.WriteLine("Mediator reacts on A and triggers folowing operations:");
this._component2.DoC();
}
if (ev == "D")
{
Console.WriteLine("Mediator reacts on D and triggers following operations:");
this._component1.DoB();
this._component2.DoC();
}
}
}
// Базовый Компонент обеспечивает базовую функциональность хранения
// экземпляра посредника внутри объектов компонентов.
class BaseComponent
{
protected IMediator _mediator;
public BaseComponent(IMediator mediator = null)
{
this._mediator = mediator;
}
public void SetMediator(IMediator mediator)
{
this._mediator = mediator;
}
}
// Конкретные Компоненты реализуют различную функциональность. Они не
// зависят от других компонентов. Они также не зависят от каких-либо
// конкретных классов посредников.
class Component1 : BaseComponent
{
public void DoA()
{
Console.WriteLine("Component 1 does A.");
this._mediator.Notify(this, "A");
}
public void DoB()
{
Console.WriteLine("Component 1 does B.");
this._mediator.Notify(this, "B");
}
}
class Component2 : BaseComponent
{
public void DoC()
{
Console.WriteLine("Component 2 does C.");
this._mediator.Notify(this, "C");
}
public void DoD()
{
Console.WriteLine("Component 2 does D.");
this._mediator.Notify(this, "D");
}
}
class Program
{
static void Main(string[] args)
{
// Клиентский код.
Component1 component1 = new Component1();
Component2 component2 = new Component2();
new ConcreteMediator(component1, component2);
Console.WriteLine("Client triggets operation A.");
component1.DoA();
Console.WriteLine();
Console.WriteLine("Client triggers operation D.");
component2.DoD();
}
}
}
Client triggers operation A. Component 1 does A. Mediator reacts on A and triggers following operations: Component 2 does C. Client triggers operation D. Component 2 does D. Mediator reacts on D and triggers following operations: Component 1 does B. Component 2 does C.