Mediator Design Pattern in C#

Mediator Design Pattern in C# with Real-Time Examples

In this article, I will discuss the Mediator Design Pattern in C# with Real-Time Examples. Please read our previous article discussing the Interpreter Design Pattern in C# with Examples. The Mediator Design Pattern falls under the category of Behavioral Design Pattern. As part of this article, we will discuss the following pointers in detail.

  1. What is the Mediator Design Pattern?
  2. Real-Time Example of Mediator Design Pattern – Facebook Group and ATC
  3. Implementing the Mediator Design Pattern in C#
  4. Understanding the Class or UML Diagram of Mediator Design Pattern
  5. Advantages of Mediator Design Pattern.
  6. When to Use Mediator Design Pattern in C#?
What is the Mediator Design Pattern?

According to the Gang of Four definitions, define an object that encapsulates how a set of objects interact with each other. Mediator promotes loose coupling by keeping objects from explicitly referring to each other and letting you vary their interaction independently.

The Mediator Design Pattern reduces the communication complexity between multiple objects. This design pattern provides a mediator object, which will be responsible for handling all the communication complexities between different objects.

The Mediator Design Pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object. This pattern is used to centralize complex communications and control between related objects in a system. The Mediator object acts as the communication center for all objects. That means when an object needs to communicate with another object, it does not call the other object directly. Instead, it calls the mediator object, and it is the responsibility of the mediator object to route the message to the destination object.

Example to Understand Mediator Design Pattern:

Let us understand the Mediator Design Pattern in C# with an Example. Please have a look at the following diagram. As shown in the diagram below, we have four objects (Object A, Object B, Object C, and Object D). And these four objects want to communicate with each other. Suppose Object A wants to communicate with Object B, then Object A should know the reference of Object B, and using that reference, Object A can call the method of Object B. Similarly if Object B wants to send some message to Object C. It should know the reference of Object C, and using that reference, it will call the methods of Object C and send the message.

Why do we need the Mediator Design Pattern?

The couplings between the objects are more, i.e., tight coupling. A lot of object knows each other. Now, in the above image, four objects are there. In real-time, you may have thousands of objects, and those thousands of objects want to communicate with each other. Then, writing code and maintaining code is very difficult.

How do we Reduce the Coupling Between the Objects?

Using the Mediator Design Pattern, we can reduce the coupling between objects. To understand this, please have a look at the following diagram. As shown in the image below, here we introduce the Mediator object. Each object has to send messages to the mediator object. The mediator object here will receive the message from each object and route that message to the destination object. So, the mediator object will take care of handling the messages. So, in this scenario, we can use the Mediator Design Pattern.

What is the Mediator Design Pattern in C#?

Real-Time Example to Understand Mediator Design Pattern – Facebook Group

Please have a look at the following diagram. Nowadays, everybody is aware of Facebook. On Facebook, we can create some specific groups; in those groups, people can join and share their knowledge. On Facebook, there is a group called Dot Net Tutorials, and in that group, many people (such as Ram, Sam, Pam, Dave, Steve, Anurag, etc.) are joined. Ram is sharing some messages in the Dot Net Tutorials group. Then, what this Facebook Group (Dot Net Tutorials) will do is it will send that message to all the members who have joined this group. So, here, the Facebook group is acting as a Mediator.

Facebook Group

Implementing Mediator Design Pattern Real-Time Example in C#:

Let us first implement the above Facebook Example using the Mediator Design Pattern in C# step by step. Then, we will understand the UML or Class Diagram of the Mediator Design Pattern by comparing it with our Example so that you can easily understand the different components involved in the Mediator Design Pattern.

Step1: Creating Mediator

This will be an Interface that defines operations that colleague objects can call for communication. So, Create an interface named FacebookGroupMediator.cs and copy and paste the following code. This interface declares two abstract methods (i.e., SendMessage and RegisterUser).

namespace MediatorDesignPattern
{
    //Mediator Interface
    //This is going to be an Interface that defines operations 
    //which can be called by colleague objects for communication.
    public interface IFacebookGroupMediator
    {
        //This Method is used to send the Message who are registered with the Facebook Group
        void SendMessage(string msg, User user);
        //This method is used to register a user with the Facebook Group
        void RegisterUser(User user);
    }
}
Step2: Creating ConcreteMediator

This will be a class implementing the communication operations of the Mediator Interface. So, Create a class file named ConcreteFacebookGroupMediator.cs and then copy and paste the following code. This class implements the FacebookGroupMediator interface and provides implementations for the SendMessage and RegisterUser abstract methods. The RegisterUser method will add the user to the particular Facebook group, i.e., adding the user to the UsersList variable. On the other hand, whenever any user shares any message in the group, the SendMessage method will receive that message, and it will send that message to all the users who are registered in the group except the user who shares the message. 

using System.Collections.Generic;
namespace MediatorDesignPattern
{
    // Concrete Mediator
    // This is going to be a class implementing the communication operations of the Mediator Interface.
    public class ConcreteFacebookGroupMediator : IFacebookGroupMediator
    {
        //The following variable is going to hold the list of objects to whom we want to communicate
        private List<User> UsersList = new List<User>();

        //The following method simply registers the user with Mediator
        public void RegisterUser(User user)
        {
            //Adding the user
            UsersList.Add(user);
            //Registering the user with Mediator
            user.Mediator = this;
        }

        //The following method is going to send the message in the group i.e. to the group users
        public void SendMessage(string message, User user)
        {
            foreach (User u in UsersList)
            {
                //Message should not be received by the user sending it
                if (u != user)
                {
                    u.Receive(message);
                }
            }
        }
    }
}
Step 3: Creating the Colleague

This will be an abstract class that defines a property that holds a reference to a mediator. So, create an abstract class named User.cs and copy and paste the following code. This abstract class has two abstract methods, i.e., Send and Receive. 

namespace MediatorDesignPattern
{
    // Colleague
    // This is going to be an abstract class that defines a property that holds a reference to a mediator.    
    public abstract class User
    {
        //This Property holds the name of the user
        protected string Name;

        //This Property is going to set and get the Mediator Instance
        //This Property value is going to be set when we register a user with the Mediator
        public IFacebookGroupMediator Mediator { get; set; }
        
        //Initializing the name using Constructor
        public User(string name)
        {
            this.Name = name;
        }

        //The following Methods are going to be Implemented by the Concrete Colleague
        public abstract void Send(string message);
        public abstract void Receive(string message);
    }
}
Step4: Creating Concrete Colleague

These are the classes that communicate with each other via the mediator. Create a class file named ConcreteUser.cs and copy and paste the following code. This is a concrete class that implements the User abstract class and provides the implementation for the Send and Receive abstract methods. The Send method sends a message to a particular Facebook group. The Receive method receives a message from a particular Facebook group. 

using System;
namespace MediatorDesignPattern
{
    //Concrete Colleague
    //These are the classes that communicate with each other via the mediator.
    public class ConcreteUser : User
    {
        //Parameterized Constructor is required to set the base class Name Property
        public ConcreteUser(string Name) : base(Name)
        {
        }

        //Overriding the Receive Method
        //This method is going to use by the Mediator to send the message to each member of the group
        public override void Receive(string message)
        {
            Console.WriteLine(this.Name + ": Received Message:" + message);
        }

        //This method is used to send the message to the Mediator by a user
        public override void Send(string message)
        {
            Console.WriteLine(this.Name + ": Sending Message=" + message + "\n");
            Mediator.SendMessage(message, this);
        }
    }
}
Step5: Client

The Main method of the Program class is going to be the client. So, please modify the Main method of the Program class as shown below. Here, we create the FacebookGroupMediator object, create several Facebook users, and then register all the users to the mediator object. Then, we send a message to the Mediator using the Dave and Rajesh user. The following client code is self-explained, so please go through the comment lines for a better understanding.

using System;
namespace MediatorDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            //Create an Instance of Mediator i.e. Creating a Facebook Group
            IFacebookGroupMediator facebookMediator = new ConcreteFacebookGroupMediator();

            //Create instances of Colleague i.e. Creating users
            User Ram = new ConcreteUser("Ram");
            User Dave = new ConcreteUser("Dave");
            User Smith = new ConcreteUser("Smith");
            User Rajesh = new ConcreteUser("Rajesh");
            User Sam = new ConcreteUser("Sam");
            User Pam = new ConcreteUser("Pam");
            User Anurag = new ConcreteUser("Anurag");
            User John = new ConcreteUser("John");

            //Registering the users with the Mediator i.e. Facebook Group
            facebookMediator.RegisterUser(Ram);
            facebookMediator.RegisterUser(Dave);
            facebookMediator.RegisterUser(Smith);
            facebookMediator.RegisterUser(Rajesh);
            facebookMediator.RegisterUser(Sam);
            facebookMediator.RegisterUser(Pam);
            facebookMediator.RegisterUser(Anurag);
            facebookMediator.RegisterUser(John);

            //One of the users Sending one Message in the Facebook Group
            Dave.Send("dotnettutorials.net - this website is very good to learn Design Pattern");
            Console.WriteLine();

            //Another user Sending another Message in the Facebook Group
            Rajesh.Send("What is Design Patterns? Please explain ");

            Console.Read();
        }
    }
}
Output:

Mediator Design Pattern in C# with Examples

Use Cases of Mediator Design Pattern:
  • When a set of objects communicate in well-defined but complex ways, and you want to centralize this communication to make it more manageable and understandable.
  • In a system where multiple classes interact, but the interactions are unstructured or complex.
Understanding the Class or UML Diagram of Mediator Design Pattern in C#:

Let us understand the Class Diagram or UML Diagram of the Mediator Design Pattern in C# and the components involved. Please have a look at the following image.

Class or UML Diagram of Mediator Design Pattern

As shown in the above UML or Class Diagram, four participants are involved in the Mediator Design Pattern in C#. Their role and responsibilities are as follows:

  1. Mediator:  This will be an Interface that defines the operations that colleague objects can call for communication. In our example, it is the IFacebookGroupMediator Interface.
  2. ConcreteMediator:  This class will implement the communication operations of the Mediator Interface. In our example, it is the ConcreteFacebookGroupMediator class.
  3. Colleague: It is an abstract class, and Concrete Colleague classes will implement this abstract class. In our example, it is the User abstract class.
  4. ConcreteColleage1 / ConcreteColleage2: These are classes that implement the Colleague interface. If a concrete colleague (let’s say ConcreteColleage1) wants to communicate with another concrete colleague (let’s say ConcreteColleage2), they will not communicate directly; instead, they will communicate via the ConcreteMediator. In our example, it is the ConcreteUser class.
Real-Time Example of Mediator Design Pattern – ATC

ATC stands for Air Traffic Control. The ATC mediator is nothing but the Air Traffic Control Tower, which is available at the Airport. Please have a look at the following image. Here, you can see different flights (such as Flight 101, Flight 202, Flight 707, and Flight 808).

Suppose Flight 101 wants to land at a particular terminal in the Airport. Then, the Flight Pilot will communicate with the ATC Mediator and say he wants to land Flight 101 at the particular airport terminal. Then, the ATC Mediator will check whether any flight is at that particular terminal. If no flight is there, then what the ATC mediator will do is it will send a message to Pilots of other flights saying that Flight 101 is going to land and you should not land at that particular terminal. Then, the ATC mediator sends a message to the Flight 101 pilot, saying you can land your flight at the particular airport terminal. Once the Flight 101 pilot gets the ATC Mediator’s confirmation message, he will land the flight at that terminal.

Air Traffic Control Tower

So, here, the ATC mediator will act as a central point, and all flights should communicate with the ATC mediator. So, what the ATC mediator will do is it will receive the message and route the message to the appropriate destinations. Here, destinations are flights.

Note: The pilots of the planes that are approaching or departing the terminal area communicate with the tower rather than explicitly communicating with one another. The tower enforces the constraints on who can take off or land. It is important to note that the tower does not control the whole flight. It exists only to enforce constraints in the terminal area.

Advantages of Mediator Design Pattern:
  • Reduced Complexity: It centralizes complex communications and controls logic between objects in a system.
  • Decoupled Objects: Colleague objects are less coupled to each other, which increases maintainability and reusability.
  • Simplified Object Protocols: Objects no longer communicate directly with each other but instead through a mediator, simplifying their interaction protocols.
  • Centralized Control: The mediator encapsulates the logic for how objects interact, making it easier to change the logic independently of the colleague classes.
When to Use Mediator Design Pattern in C#?

The Mediator Design Pattern in C# is particularly useful in scenarios where:

  • Reducing Class Interdependencies: When you have a set of classes that communicate directly with each other in complex ways, leading to high coupling and dependencies. The Mediator pattern centralizes this communication in a mediator object, reducing direct class-to-class dependencies.
  • Simplifying Object Communication: If an application has numerous components or objects that need to interact, but you want to avoid having them communicate directly in a tightly coupled manner. A mediator can act as a hub for communication and simplify interactions.
  • Reusing Components: When you need to reuse components in different contexts or frameworks, and the direct communication between components would hinder this reusability. The mediator makes each component more modular and easier to reuse.
  • Implementing Complex Communication Logic: In scenarios where the communication logic between objects is complex and needs to be centralized. The mediator encapsulates this logic, making it easier to maintain and update.
  • Decoupling Components in a GUI Application: In GUI applications, for managing complex interactions between various GUI components without them needing to know about each other, thus making them easier to manage and extend.
  • Changing Behavior Dynamically: When the behavior of an application needs to change dynamically based on certain conditions. A mediator can orchestrate these changes more efficiently by controlling the interaction between objects.
  • Creating an Extensible Framework: In the development of frameworks where you expect future extensions and modifications and where having a central point of control or communication would be beneficial.

In the next article, I will discuss the Real-Time Examples of the Mediator Design Pattern in C#. Here, in this article, I try to explain the Mediator Design Pattern in C# with Real-Time Examples. I hope you now understand the need and use of the Mediator Design Pattern in C# with Real-Time Examples.

Leave a Reply

Your email address will not be published. Required fields are marked *