The Mediator pattern is another GoF design pattern that controls how objects interact with one another (making it a behavioral pattern).
Goal
The mediator’s role is to manage the communication between objects (colleagues). Those colleagues should not communicate together directly but use the mediator instead. The mediator helps break tight coupling between these colleagues.A mediator is a middleman who relays messages between colleagues.
Design
Let’s start with some UML diagrams. From a very high level, the Mediator pattern is composed of a mediator and colleagues:

Figure 16.3: Class diagram representing the Mediator pattern
When an object in the system wants to send a message to one or more colleagues, it uses the mediator. Here is an example of how it works:

Figure 16.4: Sequence diagram of a mediator relaying messages to colleagues
That is also valid for colleagues; a colleague must also use the mediator if they need to talk to each other, as depicted in the following class diagram:

Figure 16.5: Class diagram representing the Mediator pattern including colleagues’ collaboration
In this diagram, ConcreteColleague1 is a colleague but also the consumer of the mediator. For example, that colleague could send a message to another colleague using the mediator, like this:

Figure 16.6: Sequence diagram representing colleague1 communicating with colleague2 through the mediator
From a mediator standpoint, its implementation most likely contains a collection of colleagues to communicate with, like this:

Figure 16.7: Class diagram representing a simple hypothetical concrete mediator implementation
Now that we have explored some UML diagrams, let’s look at some code.
Project – Mediator (IMediator)
The Mediator project consists of a simplified chat system using the Mediator pattern. Let’s start with the interfaces:
namespace Mediator;
public interface IMediator
{
void Send(Message message);
}
public interface IColleague
{
string Name { get; }
void ReceiveMessage(Message message);
}
public record class Message(IColleague Sender, string Content);
The system is composed of the following:
- The IMediator interface represents a mediator that can send messages to colleagues.
- The IColleague interface represents a colleague that can receive messages. It also has a Name property so we can output meaningful values.
- The Message class represents a message sent by an IColleague implementation.
Next, we implement the IMediator interface in the ConcreteMediator class, which broadcasts the messages to all IColleague instances:
public class ConcreteMediator : IMediator
{
private readonly List<IColleague> _colleagues;
public ConcreteMediator(params IColleague[] colleagues)
{
ArgumentNullException.ThrowIfNull(colleagues);
_colleagues = new List<IColleague>(colleagues);
}
public void Send(Message message)
{
foreach (var colleague in _colleagues)
{
colleague.ReceiveMessage(message);
}
}
}
That mediator is simple; it forwards all the messages it receives to every colleague it knows. The last part of the pattern is the ConcreteColleague class which lets an instance of the IMessageWriter<TMessage> interface output the messages (we explore that interface next):
public class ConcreteColleague : IColleague
{
private readonly IMessageWriter<Message> _messageWriter;
public ConcreteColleague(string name, IMessageWriter<Message> messageWriter)
{
Name = name ??
throw new ArgumentNullException(nameof(name));
_messageWriter = messageWriter ??
throw new ArgumentNullException(nameof(messageWriter));
}
public string Name { get; }
public void ReceiveMessage(Message message)
{
_messageWriter.Write(message);
}
}
Leave a Reply