Design Patterns Simplified: Mediator - Part 15

I am here to continue the discussion around Design Patterns. Today we will go through one of the behavioral design patterns called Mediator.

Links to previous posts:

Before talking about its implementation let’s begin with defining it.

As per the GOF guys, Mediator pattern is defined as follows:

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.

Well! Let’s understand what they mean and where this pattern can fit.

As the name suggests, Mediator pattern is used to enable interactions between source and target objects. It introduces a mediator object between source and target to avoid having explicit or direct references.

In a scenario when a lot of combinations take place between source and target and the number of interacting objects are growing over time, it becomes difficult to maintain code, especially for any new change.

Mediator pattern helps in such cases by encapsulating the communication logic in mediator object and thus supporting clean and loosely coupled design.



Mediator pattern has some key components as follows:

  • Mediator
    Defines an interface to facilitate communication among Colleague objects.

  • ConcreteMediator
    Implements Mediator and coordinates with colleague classes.

  • ColleagueInterface
    Interface or abstract class that defines common methods and properties to interact with colleagues.

  • ColleagueClasses
    Concrete colleague classes that are used by mediator.

How Mediator pattern works

Let’s understand this by a simple example.

We will start by defining Colleague abstract class. We can also use interface but I have taken abstract class to reuse properties.

  1. ///<summary>  
  2. /// Abstract Colleague class  
  3. ///</summary>  
  4.   
  5. abstract class Team  
  6. {  
  7.     private string _name;  
  8.     public Team(string name)  
  9.     {  
  10.         _name = name;  
  11.     }  
  12.     public string Name  
  13.     {  
  14.         get  
  15.         {  
  16.             return _name;  
  17.         }  
  18.     }  
  19.     public MeetingRoom MeetingRoom  
  20.     {  
  21.         get;  
  22.         set;  
  23.     }  
  24.     public void SendMessage(string to, string message)  
  25.     {  
  26.         MeetingRoom.SendMessage(_name, to, message);  
  27.     }  
  28.     public virtual void ReceiveMessage(string from, string message)  
  29.     {  
  30.         Console.WriteLine("Message from {0} to {1}: {2}", from, _name, message);  
  31.     }  
  32. }  
Now let’s create a couple of concrete classes as follows.
  1. ///<summary>  
  2. /// Concreate Colleague class  
  3. ///</summary>  
  4.   
  5. class DevTeam: Team  
  6. {  
  7.     public DevTeam(string name): base(name)  
  8.     {}  
  9.     public override void ReceiveMessage(string from, string message)  
  10.     {  
  11.         base.ReceiveMessage(from, message);  
  12.     }  
  13. }  
  14.   
  15. ///<summary>  
  16. /// Concreate Colleague class  
  17. ///</summary>  
  18.   
  19. class TestTeam: Team  
  20. {  
  21.     public TestTeam(string name): base(name)  
  22.     {}  
  23.     public override void ReceiveMessage(string from, string message)  
  24.     {  
  25.         base.ReceiveMessage(from, message);  
  26.     }  
  27. }  
The time has come to create Mediator objects. Let’s create interface first. 
  1. ///<summary>  
  2. /// Mediator Interface.  
  3. ///</summary>  
  4. interface IMeetingRoom  
  5. {  
  6.    void Register(Team team);  
  7.    void SendMessage(string from, string to, string message);  
  8. }  
In the interface above you can see that Register method is holding the reference of the colleague class (i.e. Team).

Now we will create concrete mediator class that will be used to establish the communication among colleagues.
  1. ///<summary>  
  2. /// Mediator concreate class.  
  3. ///</summary>  
  4. class MeetingRoom: IMeetingRoom  
  5. {  
  6.     private List<Team> _teams = new List<Team>();  
  7.     public void Register(Team member)  
  8.     {  
  9.         _teams.Add(member);  
  10.         member.MeetingRoom = this;  
  11.     }  
  12.     public void SendMessage(string from, string to, string message)  
  13.     {  
  14.         Team team = _teams.Where(t => t.Name == to)  
  15.             .First();  
  16.         if (team != null)  
  17.         {  
  18.             team.ReceiveMessage(from, message);  
  19.         }  
  20.     }  
  21. }  
As you can see above in the MeetingRoom class we have created a collection to store teams while registration and same collection is used to provide appropriate team object in the SendMessage method. Subsequently team object is used to invoke ReceiveMessage dynamically or in polymorphic fashion.
 
Now let's see how client uses the setup created so far.
 
public class Program
{
static void Main()
{
Console.Title = "Mediator pattern Demo";

Team seTeam = new DevTeam("SETeam");
Team dbTeam = new DevTeam("DBTeam");
Team sysTestTeam = new TestTeam("SysTestTeam");
Team intTestTeam = new TestTeam("IntTestTeam");

MeetingRoom meetingRoom = new MeetingRoom();
meetingRoom.Register(seTeam);
meetingRoom.Register(dbTeam);
meetingRoom.Register(sysTestTeam);
meetingRoom.Register(intTestTeam);

seTeam.SendMessage(sysTestTeam.Name, "Hello System Testing Team!");
dbTeam.SendMessage(intTestTeam.Name, "Hello Integration Testing Team!");
seTeam.SendMessage(dbTeam.Name, "Hello Database Team!");
sysTestTeam.SendMessage(seTeam.Name, "Hello System Engineering Team!"); 
}
}
 
Output


As you can see that first we are creating colleague (i.e. Team) objects and then registering them into Mediator (i.e. MeetingRoom)
 
Summary:
In the article, we have gone through what mediator pattern is and where it should be used. We have also seen an example to understand how to use it. Please don’t confuse between Mediator pattern and Adapter as the purpose or intent of both the patterns are different. Adapter pattern at one side used to make interactions between two incompatible components whereas Mediator pattern simplifies the complexity involved in the communication of various components.

You can also download the attached demo project (MediatorPatternDemo.zip) to go through the full source code referred to in the article.

Hope you have liked the article. I look forward for your comments/suggestions.