What is the Mediator Design Pattern?

Göksu Deniz
8 min readDec 23, 2022

--

Image is created by DALL-E 2 AI

The Mediator design pattern is a behavioral design pattern that allows you to define a separate component (the mediator) that controls the interaction between a set of objects. The mediator reduces the dependencies between the objects and helps to promote loose coupling between them.

The mediator pattern is useful when you have a set of objects that need to communicate with each other and you want to decouple those objects from each other to reduce the complexity of their interactions. Instead of having each object communicate directly with the others, the objects communicate with the mediator, which then communicates with the other objects on their behalf. This can make it easier to change the interactions between the objects, since you only need to modify the mediator rather than modifying the objects themselves.

Let’s say you are organizing a school event and you want to invite a variety of different groups to participate. You have a group of students who are interested in performing a dance, a group of students who want to set up a craft table, and a group of students who want to lead a science experiment.

To help coordinate the different groups and make sure everything runs smoothly at the event, you can act as the mediator. This means that instead of each group communicating directly with each other about their plans for the event, they can go through you. You can listen to each group’s ideas and make sure that everyone’s needs are taken into account when planning the event.

By acting as the mediator, you can help to reduce conflicts and ensure that everyone’s ideas are heard and included in the final plan. This can make it easier for the different groups to work together and contribute to the event, rather than getting into disagreements or misunderstandings.

using System;

namespace MediatorExample
{
// The Mediator interface declares a method used by components to notify the
// mediator about various events. The Mediator may react to these events and
// pass the execution to other components.
public interface IMediator
{
void SendMessage(string message, Colleague colleague);
}

// The ConcreteMediator class coordinates interactions between components by
// sending messages between them.
public class ConcreteMediator : IMediator
{
private Colleague1 _colleague1;
private Colleague2 _colleague2;

public ConcreteMediator(Colleague1 colleague1, Colleague2 colleague2)
{
this._colleague1 = colleague1;
this._colleague1.SetMediator(this);
this._colleague2 = colleague2;
this._colleague2.SetMediator(this);
}

public void SendMessage(string message, Colleague colleague)
{
if (colleague == this._colleague1)
{
this._colleague2.ReceiveMessage(message);
}
else
{
this._colleague1.ReceiveMessage(message);
}
}
}

// The Colleague class declares a method used by the Mediator to send and
// receive messages.
public abstract class Colleague
{
protected IMediator _mediator;

public Colleague(IMediator mediator)
{
this._mediator = mediator;
}

public void SetMediator(IMediator mediator)
{
this._mediator = mediator;
}

public abstract void SendMessage(string message);
public abstract void ReceiveMessage(string message);
}

// Concrete Colleagues communicate with each other indirectly via the Mediator.
public class Colleague1 : Colleague
{
public Colleague1(IMediator mediator) : base(mediator)
{
}

public override void SendMessage(string message)
{
this._mediator.SendMessage(message, this);
}

public override void ReceiveMessage(string message)
{
Console.WriteLine("Colleague 1 received message: " + message);
}
}

public class Colleague2 : Colleague
{
public Colleague2(IMediator mediator) : base(mediator)
{
}

public override void SendMessage(string message)
{
this._mediator.SendMessage(message, this);
}

public override void ReceiveMessage(string message)
{
Console.WriteLine("Colleague 2 received message: " + message);
}
}
}

The IMediator interface declares a method called SendMessage that is used by the Colleague objects to send messages to each other. The ConcreteMediator class implements this interface and provides the specific logic for how the messages are passed between the Colleague objects.

The Colleague class is an abstract base class that defines the interface for communication with the mediator. It has two abstract methods: SendMessage and ReceiveMessage. The ConcreteColleague1 and ConcreteColleague2 classes are concrete implementations of the Colleague class and provide the specific implementation for the SendMessage and ReceiveMessage methods.

When a Colleague object wants to send a message to the other object, it calls the SendMessage method on itself, which in turn calls the SendMessage method on the mediator. The mediator then determines which Colleague object should receive the message and calls the ReceiveMessage method on that object.

Then you can use this pattern on where you want like this below:

class Program
{
static void Main(string[] args)
{
// The client code.
Colleague1 colleague1 = new Colleague1(new ConcreteMediator());
Colleague2 colleague2 = new Colleague2(new ConcreteMediator());

colleague1.SendMessage("Hello, world!");
colleague2.SendMessage("Hi there!");
}
}

In this code, we create two Colleague objects, colleague1 and colleague2, and pass a ConcreteMediator object to each of their constructors. Then, we use the SendMessage method on each of the Colleague objects to send messages to the other object. When a message is sent, the SendMessage method of the ConcreteMediator is called, which then sends the message to the other Colleague object.

Imagine you are building a chat application that allows users to send messages to each other. You want to design the application in such a way that the user objects are decoupled from each other, so that you can add or remove users from the chat without having to make changes to the other user objects.

To do this, you can use the mediator pattern. You can create a ChatMediator class that acts as the mediator between the user objects. The ChatMediator class will have a list of all the users in the chat and will provide methods for sending messages between the users.

Here’s what the code for this might look like in C#:

using System;
using System.Collections.Generic;

namespace MediatorExample
{
// The Mediator interface declares a method used by components to notify the
// mediator about various events. The Mediator may react to these events and
// pass the execution to other components.
public interface IChatMediator
{
void SendMessage(string message, User sender);
void AddUser(User user);
}

// The ConcreteMediator class coordinates interactions between components by
// sending messages between them.
public class ChatMediator : IChatMediator
{
private List<User> _users;

public ChatMediator()
{
this._users = new List<User>();
}

public void SendMessage(string message, User sender)
{
foreach (User user in this._users)
{
if (user != sender)
{
user.ReceiveMessage(message);
}
}
}

public void AddUser(User user)
{
this._users.Add(user);
}
}

// The Colleague class declares a method used by the Mediator to send and
// receive messages.
public abstract class User
{
protected IChatMediator _mediator;

public User(IChatMediator mediator)
{
this._mediator = mediator;
}

public void SendMessage(string message)
{
this._mediator.SendMessage(message, this);
}

public abstract void ReceiveMessage(string message);
}

// Concrete Colleagues communicate with each other indirectly via the Mediator.
public class ChatUser : User
{
public ChatUser(IChatMediator mediator, string name) : base(mediator)
{
this.Name = name;
}

public string Name { get; set; }

public override void ReceiveMessage(string message)
{
Console.WriteLine("{0} received message: {1}", this.Name, message);
}
}
}

The IChatMediator interface declares a method called SendMessage that is used by the ChatUser objects to send messages to each other. The ChatMediator class implements this interface and provides the specific logic for how the messages are passed between the ChatUser objects. It also has a AddUser method for adding new users to the chat.

The User class is an abstract base class that defines the interface for communication with the mediator. It has a single abstract method called ReceiveMessage. The ChatUser class is a concrete implementation of the User class and provides the specific implementation for the ReceiveMessage method. It also has a Name property to identify the user.

When a ChatUser object wants to send a message to the other users, it calls the SendMessage method on itself, which in turn calls the SendMessage method on the mediator. The mediator then sends the message to all the ChatUser objects except for the sender. Each ChatUser object receives the message by calling the ReceiveMessage method on itself.

Now, you are ready to use it:

class Program
{
static void Main(string[] args)
{
// The client code.
IChatMediator mediator = new ChatMediator();

ChatUser user1 = new ChatUser(mediator, "Alice");
ChatUser user2 = new ChatUser(mediator, "Bob");
ChatUser user3 = new ChatUser(mediator, "Charlie");

mediator.AddUser(user1);
mediator.AddUser(user2);
mediator.AddUser(user3);

user1.SendMessage("Hi, everyone!");
user2.SendMessage("Hello, Alice!");
user3.SendMessage("Hey, what's up?");
}
}

In this code, we create a ChatMediator object and three ChatUser objects. We add the ChatUser objects to the mediator and then use the SendMessage method on each of the ChatUser objects to send messages to the other users. When a message is sent, the SendMessage method of the ChatMediator is called, which then sends the message to the other ChatUser objects.

Conclusion

The mediator design pattern is used to provide a centralized communication channel between objects. It allows objects to communicate with each other indirectly, through the mediator, rather than communicating directly with each other.

There are several benefits to using the mediator pattern:

  1. It reduces the complexity of the objects by removing the need for them to have direct references to each other. This can make it easier to add or remove objects from the system, as they only need to be registered with the mediator.
  2. It promotes loose coupling between the objects. The objects don’t need to know about the specific implementation details of each other, they only need to know about the mediator and how to communicate with it. This can make it easier to modify or extend the system.
  3. It can improve the encapsulation of the objects by hiding the details of their communication from each other. This can make it easier to change the way the objects communicate without affecting their internal implementation.
  4. It can make it easier to add new functionality to the system. If a new object needs to be added to the system, it can simply be registered with the mediator and start communicating with the other objects through it.

Overall, the mediator pattern can help to simplify the design of systems with many objects by providing a central point of communication between them. It can make it easier to add, remove, and modify objects in the system, and can improve the encapsulation and modularity of the objects.

Here are a few real-world scenarios where the mediator design pattern might be useful:

  1. Chat application: As mentioned in the previous example, a chat application can use the mediator pattern to allow users to send messages to each other. The mediator would be responsible for forwarding the messages to the appropriate users and handling any additional functionality such as logging or filtering.
  2. Air traffic control system: An air traffic control system might use the mediator pattern to coordinate the movement of planes in the airspace. The mediator would be responsible for ensuring that planes are kept at safe distances from each other and for routing them to their destinations.
  3. Online auction site: An online auction site could use the mediator pattern to facilitate communication between buyers and sellers. The mediator would be responsible for handling the bidding process and ensuring that the appropriate parties are notified when a bid is placed or an item is sold.
  4. Computer network: A computer network might use the mediator pattern to allow devices to communicate with each other. The mediator would be responsible for routing messages between the devices and handling any additional functionality such as security or error correction.
  5. Online gaming platform: An online gaming platform could use the mediator pattern to allow players to interact with each other. The mediator would be responsible for handling communication between the players and facilitating the gameplay experience.

These are just a few examples, but the mediator pattern can be useful in a wide variety of scenarios where there is a need to coordinate communication between multiple objects.

Thanks for reading! If you found the article helpful, you can clap and follow. So you will notified of new articles.

# Reference

It was created with the help of ChatGPT AI.

--

--

Göksu Deniz

Software Engineer, passionate about creating efficient solutions. Skilled in mentoring teams to deliver successful projects. Always exploring new tech trends.