Skip to main content

Command Palette

Search for a command to run...

Mediator Pattern in C#

Simplifying Communication Between Objects

Published
•5 min read•View as Markdown
Mediator Pattern in C#
S

Experienced .NET developer, with deep understanding of the intricacies of the platform having worked on a wide range of projects.

Problem statement

It solves the problem of complex communication between objects, which can lead to tight coupling and difficult-to-maintain code. When objects communicate with each other directly, they create dependencies that make it hard to change the code without affecting other parts of the system.

Communication between objects can quickly become complex and hard to maintain. That's where the mediator pattern comes in - simplifying communication and reducing dependencies between objects.

Introduction

The mediator pattern is a behavioral design pattern that promotes loose coupling between objects by limiting their direct communication and providing a mediator object that handles communication between them. It acts as a middleman, facilitating communication between objects without them knowing about each other.

Implementation

First, let's set up the problem: imagine a restaurant where orders are taken by the waitstaff and prepared by the kitchen staff. However, the waitstaff and kitchen staff don't communicate with each other very effectively, which leads to mistakes and delays in getting food to customers.

Let's see, How can we use the mediator pattern to improve communication between the waitstaff and kitchen staff?

Step-1 Let's define an interface for the mediator that the waitstaff and kitchen staff will use to communicate with each other. The IRestaurantMediator interface might look like this-

public interface IRestaurantMediator
{
    void SendMessage(string sender, string message);
    void AddKitchenStaff(string staff);
    void AddWaitStaff(string staff);
}

The SendMessage method will be used to send messages between the waitstaff and kitchen staff, while the AddKitchenStaff and AddWaitStaff methods will be used to register new staff members with the mediator.

Step-2 Create a concrete implementation of the mediator. The RestaurantMediator class might look like this-

public class RestaurantMediator : IRestaurantMediator
{
    private List<string> _kitchenStaff = new List<string>();
    private List<string> _waitStaff = new List<string>();

    public void SendMessage(string sender, string message)
    {
        // TODO: Implement message sending between staff members
    }

    public void AddKitchenStaff(string staff)
    {
        _kitchenStaff.Add(staff);
    }

    public void AddWaitStaff(string staff)
    {
        _waitStaff.Add(staff);
    }
}

In this implementation, we keep track of the kitchen staff and waitstaff in two separate lists. The SendMessage method is not yet implemented, but it will be used to send messages between staff members as needed.

Step-3 Let's create a class to represent the kitchen staff.

public class KitchenStaff
{
    private readonly string _name;
    private readonly IRestaurantMediator _mediator;

    public KitchenStaff(string name, IRestaurantMediator mediator)
    {
        _name = name;
        _mediator = mediator;
    }

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

    public void JoinKitchen()
    {
        _mediator.AddKitchenStaff(_name);
        Console.WriteLine($"{_name} joined the kitchen staff.");
    }
}

The KitchenStaff class has a name and a reference to the mediator. The SendMessage method allows kitchen staff members to send messages to the waitstaff, while the JoinKitchen method registers the kitchen staff member with the mediator and announces that they have joined the kitchen staff

Step-4 Finally, let's create a class to represent the waitstaff. The WaitStaff class might look like this:

public class WaitStaff
{
    private readonly string _name;
    private readonly IRestaurantMediator _mediator;

    public WaitStaff(string name, IRestaurantMediator mediator)
    {
        _name = name;
        _mediator = mediator;
    }

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

    public void JoinWaitStaff()
    {
        _mediator.AddWaitStaff(_name);
        Console.WriteLine($"{_name} joined the wait staff.");
    }
}

The WaitStaff class has a name and a reference to the mediator. The `SendMessage.

Step-5

var mediator = new RestaurantMediator();
var kitchenStaff1 = new KitchenStaff("Chef Alice", mediator);
var kitchenStaff2 = new KitchenStaff("Chef Bob", mediator);
var waitStaff1 = new WaitStaff("Server Carol", mediator);
var waitStaff2 = new WaitStaff("Server Dave", mediator);

kitchenStaff1.JoinKitchen();
kitchenStaff2.JoinKitchen();
waitStaff1.JoinWaitStaff();
waitStaff2.JoinWaitStaff();

kitchenStaff1.SendMessage("Order up!");
waitStaff1.SendMessage("Table 4 needs more water.");

In this example, the IRestaurantMediator interface defines the methods that the mediator will use to send messages and add staff. The RestaurantMediator class is a concrete implementation of the mediator that keeps track of the kitchen staff and wait staff, and sends messages between them as needed.

The KitchenStaff and WaitStaff classes represent people who work in the kitchen and on the wait staff, respectively. They have a name and a reference to the mediator. The SendMessage method allows them to send messages to the other staff, while the JoinKitchen and JoinWaitStaff methods register them with the mediator and announce that they have joined their respective teams.

Advantages

Reduced coupling between objects: The Mediator pattern decouples the objects by handling their communication through a mediator object.

Easier maintenance: When a change is made to the communication protocol between objects, only the mediator object needs to be changed, rather than all the objects.

Simplified communication: The Mediator pattern simplifies communication between objects by providing a single point of communication.

Better scalability: The Mediator pattern makes it easier to add new objects to the system without affecting the existing objects.

However, the Mediator pattern can also introduce a single point of failure, since all communication goes through the mediator object. It can also add complexity to the system, as a new object needs to be created to handle the communication between the existing objects.

Challenges and limitations

Complexity: As the number of components in a system increases, the complexity of the mediator can also increase, making it more difficult to maintain and modify.

Coupling: The mediator can become a central point of control for the system, which can lead to tight coupling between components. This can make it more difficult to make changes to the system or replace components.

Single point of failure: If the mediator fails or becomes unavailable, communication between components can be disrupted.

Scalability: The mediator pattern may not be well-suited for systems that need to scale to handle a large number of components or messages.

Performance: The use of a mediator can introduce additional overhead in terms of message passing and processing, which can impact the performance of the system.

Lack of flexibility: In some cases, the mediator pattern may not be flexible enough to handle all communication scenarios between components.

In conclusion, the mediator pattern can be a useful tool for improving communication between components of a system, but it's not a one-size-fits-all solution. Like any tool, it has its limitations and challenges.