How to use the mediator design pattern in C#

Take advantage of the mediator design pattern to promote loose coupling and simplify the coding of object interactions

How to use the mediator design pattern in C#
Wanita di Indonesia

Design patterns are used to solve common design problems and reduce the complexities in our code. The mediator pattern is a behavioral design pattern that promotes loose coupling between objects and helps to organize the code for inter-object communications.

This article presents a discussion of the mediator design pattern and how it can be implemented using C#.

What is the mediator design pattern? 

Imagine an application in which there are many objects that are communicating with each other. The mediator design pattern is useful when the number of objects grows so large that it becomes difficult to maintain the references to the objects. The mediator is essentially an object that encapsulates how one or more objects interact with each other. The mediator design pattern controls how these objects communicate, and helps to reduce the number of dependencies among them that you have to manage.

In the mediator design pattern, the objects don’t communicate with one another directly but through the mediator. When an object needs to communicate with another object or a set of objects, it transmits the message to the mediator. The mediator then transmits the message to each receiver object in a form that is understandable to it.

By eliminating the direct communication between objects, the mediator design pattern promotes loose coupling. The other benefit of using the mediator design pattern is that it improves code readability and maintainability. For good measure, here is the official Gang of Four definition of the mediator pattern:

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Note that the mediator design pattern differs from the facade design pattern. The mediator pattern facilitates how a set of objects interact, while the facade pattern simply provides a unified interface to a set of interfaces in the application. Thus the mediator pattern is a behavior pattern that deals with object behavior, the facade pattern is a structural pattern that deals with object composition.

Implementing the mediator design pattern in C#

But enough of the concepts—let’s get into some code. The participants in this design are the mediator, the concrete mediator, and one or more participant types. While the mediator is responsible for defining the interface for communicating with the participants, the concrete mediator, as the name suggests, implements the mediator interface and has knowledge of the participants. Note that the participant type is sometimes called a colleague. So in some implementations, you have colleague and concrete colleague types.

Now, refer to the following interface. This is the interface for the mediator we will be using in this example.

public interface Imediator 
    {
        void AddParticipant(IParticipant participant);
        void BroadcastMessage(string message, IParticipant sender);
    }

Note that this interface contains the declaration of two methods, AddParticipant and BroadcastMessage. While the former is used to add participants to a list of participants maintained by the ConcreteMediator class (given below), the latter is used to broadcast messages to the list of the participants.

Here is the ConcreteMediator class. It implements the IMediator interface. I’m leaving the implementation of the BroadcastMessage method for you to fill in.

public class ConcreteMediator : Imediator 
    {
        List<IParticipant> participants = new List<IParticipant>();
        public void AddParticipant(IParticipant participant)
        {
            participants.Add(participant);
        }
        public void BroadcastMessage(string message, IParticipant sender)
        {
            // Write code here to broadcast the message to the participants
        }
    }

The IParticipant interface contains the declaration of the SendMessage method.

public interface Iparticipant
    {
        void SendMessage(string message);
    }

Here is an example of the concrete participant classes. Note that these classes implement the IParticipant interface.

public class ConcreteParticipant1 : Iparticipant
    {
        protected IMediator mediator;
        public ConcreteParticipant1(IMediator mediator)
        {
            this.mediator = mediator;
        }
        public void SendMessage(string message)
        {
            mediator.SendMessage(message, this);
        }
    }
    public class ConcreteParticipant2 : Iparticipant
    {
        protected IMediator mediator;
        public ConcreteParticipant2(IMediator mediator)
        {
            this.mediator = mediator;
        }
        public void SendMessage(string message)
        {
            mediator.SendMessage(message, this);
        }
    }

And that’s it! The following code snippet shows how you can use all of the types we have built thus far.

static void Main(string[] args)
    {
        IMediator mediator = new ConcreteMediator();
        IParticipant participant1 = new ConcreteParticipant1(mediator);
        IParticipant participant2 = new ConcreteParticipant2(mediator);
        mediator.AddParticipant(participant1);
        mediator.AddParticipant(participant2);
        participant1.SendMessage(“This is the first participant”);
        participant2.SendMessage(“This is the second participant”);
        Console.ReadLine();
    }

The mediator design pattern is a behavioral pattern that promotes loose coupling by mediating the communications between disparate objects. Because the mediator facilitates all interactions between objects, these objects can be changed at will. Most importantly, they don’t need to have any knowledge of one another. Thus the mediator pattern helps you write well-structured, maintainable, and easily testable code.