Mediator design pattern | Behavioral Design Pattern

Mediator design pattern


The Mediator design pattern promotes loose coupling by centralizing communication between objects. Instead of objects interacting directly, they communicate through a mediator, which handles the interactions. This helps to reduce dependencies and make the system more maintainable. Do you have any specific questions about the Mediator pattern?


In the Mediator design pattern, key components include:


1. Mediator: Acts as the central hub that coordinates communication between different components. It encapsulates the communication logic and helps in avoiding direct connections between the components.


2. Colleagues: These are the components or objects that need to communicate with each other, but instead of doing so directly, they communicate through the Mediator. Colleagues are often unaware of each other and only interact through the Mediator.


When to use the Mediator pattern:

1. Complex Interactions: Use when a set of objects need to communicate in complex ways, and having direct connections between them would result in a tangled and difficult-to-maintain system.


2. Loose Coupling: Use when you want to promote loose coupling between components. The Mediator helps avoid direct dependencies, making it easier to modify and extend the system.


Why to use the Mediator pattern:

1. Maintainability: It enhances the maintainability of the codebase by centralizing communication logic. Changes to how objects communicate can be confined to the mediator, reducing the impact on individual components.


2. Scalability: When adding new components, the Mediator pattern simplifies integration, as new objects only need to interact with the mediator rather than establishing connections with multiple existing objects.


Where to use the Mediator pattern:

1. GUI Systems: Mediators are commonly used in graphical user interface systems where various components (buttons, text fields, etc.) need to interact without having direct connections.


2. Chat Applications: In systems with multiple users or entities that need to communicate, a mediator can facilitate the communication process without each entity being aware of others.


Remember, the Mediator pattern might add some complexity, so it's beneficial in situations where the benefits of loose coupling and centralized control outweigh the overhead.


Example of the Mediator pattern in C#:


using System;

using System.Collections.Generic;


// Mediator Interface

public interface IMediator

{

    void SendMessage(string message, Colleague colleague);

}


// Concrete Mediator

public class ConcreteMediator : IMediator

{

    private List<Colleague> colleagues = new List<Colleague>();


    public void RegisterColleague(Colleague colleague)

    {

        colleagues.Add(colleague);

    }


    public void SendMessage(string message, Colleague sender)

    {

        foreach (var colleague in colleagues)

        {

            // Don't send the message back to the sender

            if (colleague != sender)

            {

                colleague.ReceiveMessage(message);

            }

        }

    }

}


// Colleague

public abstract class Colleague

{

    protected IMediator mediator;


    public Colleague(IMediator mediator)

    {

        this.mediator = mediator;

    }


    public abstract void Send(string message);

    public abstract void ReceiveMessage(string message);

}


// Concrete Colleague

public class ConcreteColleague : Colleague

{

    public ConcreteColleague(IMediator mediator) : base(mediator) { }


    public override void Send(string message)

    {

        Console.WriteLine($"Sending message: {message}");

        mediator.SendMessage(message, this);

    }


    public override void ReceiveMessage(string message)

    {

        Console.WriteLine($"Received message: {message}");

    }

}


class Program

{

    static void Main()

    {

        // Create Mediator

        var mediator = new ConcreteMediator();


        // Create Colleagues and register them with the mediator

        var colleague1 = new ConcreteColleague(mediator);

        var colleague2 = new ConcreteColleague(mediator);


        mediator.RegisterColleague(colleague1);

        mediator.RegisterColleague(colleague2);


        // Colleagues communicate through the mediator

        colleague1.Send("Hello from Colleague 1!");

        colleague2.Send("Hi there!");


        /*

        Output:

        Sending message: Hello from Colleague 1!

        Received message: Hello from Colleague 1!

        Sending message: Hi there!

        Received message: Hi there!

        */

    }

}


In this example, `ConcreteMediator` manages the communication between `ConcreteColleague` objects. Colleagues send messages through the mediator, and the mediator distributes messages to other colleagues.


Real-world example in C# using a chat application:


using System;

using System.Collections.Generic;


// Mediator Interface

public interface IChatMediator

{

    void SendMessage(string message, IUser sender);

}


// Colleague (User)

public abstract class IUser

{

    protected IChatMediator mediator;

    public string Name { get; }


    public IUser(IChatMediator mediator, string name)

    {

        this.mediator = mediator;

        this.Name = name;

    }


    public abstract void Send(string message);

    public abstract void ReceiveMessage(string message);

}


// Concrete Mediator (ChatRoom)

public class ChatRoom : IChatMediator

{

    private List<IUser> users = new List<IUser>();


    public void AddUser(IUser user)

    {

        users.Add(user);

    }


    public void SendMessage(string message, IUser sender)

    {

        foreach (var user in users)

        {

            // Don't send the message back to the sender

            if (user != sender)

            {

                user.ReceiveMessage($"{sender.Name}: {message}");

            }

        }

    }

}


// Concrete Colleague (ChatUser)

public class ChatUser : IUser

{

    public ChatUser(IChatMediator mediator, string name) : base(mediator, name) { }


    public override void Send(string message)

    {

        Console.WriteLine($"{Name} is sending message: {message}");

        mediator.SendMessage(message, this);

    }


    public override void ReceiveMessage(string message)

    {

        Console.WriteLine($"{Name} received message: {message}");

    }

}


class Program

{

    static void Main()

    {

        // Create Mediator (ChatRoom)

        var chatRoom = new ChatRoom();


        // Create Users and register them with the mediator

        var user1 = new ChatUser(chatRoom, "User1");

        var user2 = new ChatUser(chatRoom, "User2");


        chatRoom.AddUser(user1);

        chatRoom.AddUser(user2);


        // Users communicate through the mediator (ChatRoom)

        user1.Send("Hello, everyone!");

        user2.Send("Hi there!");


        /*

        Output:

        User1 is sending message: Hello, everyone!

        User2 received message: User1: Hello, everyone!

        User2 is sending message: Hi there!

        User1 received message: User2: Hi there!

        */

    }

}

```


In this example, the `ChatRoom` acts as the mediator, managing communication between `ChatUser` objects. Users send messages through the mediator, and the mediator distributes these messages to other users, promoting loose coupling between users and centralized control over the chat communication.

Comments