Use the Mediator Pattern to centralize complex communications and control between related objects.
- Tight coupling between a set of interacting objects should be avoided.
- It should be possible to change the interaction between a set of objects independently.
Tightly coupled objects are hard to implement, change, test and reuse.
- Define a
Mediator
object that encapsulates the interaction between a set of objects. - Objects delegate their interaction to a mediator object instead of interacting with each other directly.
This makes the objects loosely coupled.
The Mediator is commonly used to coordinate related GUI components.
- Mediator (IChatRoom)
- defines an interface for communicating with Colleague objects
- ConcreteMediator (ChatRoom)
- implements cooperative behavior by coordinating Colleague objects.
- knows and maintains its colleagues.
- Colleague (IParticipant)
- defines an interface for using Colleague objects.
- ConcreateColleague (Participant)
- each colleague knows its Mediator object
- each colleague communicates with its mediator whenever it would have otherwise communicated with another colleague.
Colleagues send and receive requests from a Mediator object. The mediator implements the cooperative behavior by routing request between the appropriate colleagues.
- Colleagues classes may become more reusable and are decoupled from the system.
- Simplifies maintenance of the system by centralizing control logic.
- Simplifies and reduces the variety of messages sent between objects in the system.
- Without proper design, the mediator object can become overly complex.
- Single point of failure
- Hard to test --> Objects should be mocked
Mediator
public interface IChatRoom
{
void RegisterParticipant(IParticipant participant);
void Send(String from, String to, string message);
}
ConcreteMediator
public class ChatRoom : IChatRoom
{
private readonly IDictionary<string, IParticipant> _participants = new Dictionary<string, IParticipant>();
public void RegisterParticipant(IParticipant participant)
{
this._participants.Add(participant.GetName(), participant);
}
public void Send(string from, string to, string message)
{
if (this._participants.ContainsKey(to))
{
this._participants[to].Receive(from, message);
}
else
{
throw new ArgumentException("{0} not found", to);
}
}
}
Colleague
public interface IParticipant
{
string GetName();
void Send(string to, string message);
void Receive(string from, string message);
}
ConcreteColleague
public class Participant : IParticipant
{
private readonly string _name;
private readonly IChatRoom _chatRoom;
public Participant(string name, IChatRoom chatRoom)
{
this._name = name;
this._chatRoom = chatRoom;
this._chatRoom.RegisterParticipant(this);
}
public string GetName()
{
return this._name;
}
public void Send(string to, string message)
{
this._chatRoom.Send(this._name, to, message);
}
public void Receive(string from, string message)
{
Console.WriteLine("{0} to {1}: {2}", from, this._name, message);
}
}
Usage
IChatRoom chatRoom = new ChatRoom();
IParticipant einstein = new Participant("Einstein", chatRoom);
IParticipant newton = new Participant("Newton", chatRoom);
IParticipant galileo = new Participant("Galileo", chatRoom);
newton.Send(galileo.GetName(), "I discoverd laws of motion");
einstein.Send(newton.GetName(), "I discovered how gravity works");
-
Facade differs from Mediator in that it abstracts a subsystem of objects to provide a more convenient interface. Its protocol is uni-directional (From Facade to subsystem but not vice-versa). In contrast, Mediator enables cooperative behavior that colleague objects don't or can't provide, and the protocol is multi-directional.
-
Observer - Colleagues can communicate with the mediator using the Observer pattern.