The mediator design pattern comes under the list of Behavioral design patterns. The mediator facilitates the communication b/w different objects so they don't have to keep the reference of each other. instead, they depend on a mediator to communicate with the other objects.
Definition of Mediator Design Pattern: With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead, communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.
Let's take the example of a Chat Room. One Chat Room can have multiple Team members and each message sent by any of the members will be broadcasted to all the other members. Let's say, If a Member has to send a message to Other Members it would required to have instances of all the other Team Members. You can think of this idea already, what a mess it would become as the Number of Team Members will grow.
This type of object reference mesh b/w classes can be removed using the Mediator design pattern. Here we take Chat Room as a Mediator that will hold references to all the Team Members (Colleagues). There will be bi-directional communication b/w Chat Room (Mediator) & Team Members ( Colleagues). So each Team Member ( Colleague) will have to reference to Chat Room (Mediator) instead of having the list of all the other Team Members (Colleagues). And the Chat Room (Mediator) will contain the Objects list of all the Team Members. Team Members ( Colleague) will send messages to the Chat Room (Mediator) and Chat Room (Mediator) will broadcast the message to all other Team Members (Colleagues). So the tight coupling and mesh b/w all the Team Members will no longer exist, instead, Chat Room (Mediator) will work as a Message handler b/w Team Members (Colleagues). I am putting Mediator & Colleagues words deliberately in the conversation, as these are the words that you will see over the internet if you will surf for Mediator design pattern in detail. Below is the picture diagram of Mediator Design pattern.
Let's look at some code for Mediator Design Pattern. This implementation of the Mediator Design Pattern also consists of Abstract classes. Although it's up to your requirement if you want to create Abstract classes or you only want to implement concrete implementations.
Code Based on Chat Room Application
public abstract class Chatroom { public abstract void Register(TeamMember member); public abstract void Send(string from, string message); public abstract void SendTo<T>(string from, string message) where T : TeamMember; }
Chatroom.cs
public abstract class TeamMember { private Chatroom chatroom; public TeamMember(string name) { this.Name = name; } public string Name { get; } internal void SetChatroom(Chatroom chatroom) { this.chatroom = chatroom; } public void Send(string message) { this.chatroom.Send(this.Name, message); } public virtual void Receive(string from, string message) { Console.WriteLine($"from {from}: '{message}'"); } public void SendTo<T>(string message) where T : TeamMember { this.chatroom.SendTo<T>(this.Name, message); } }
TeamMember.cs
Than we will define concreate implementations of Team Members & Chat Room implementing the above Interface classes.
public class ChatRoom1 : Chatroom { private List<TeamMember> members = new List<TeamMember>(); public override void Register(TeamMember member) { member.SetChatroom(this); this.members.Add(member); } public override void Send(string from, string message) { this.members.ForEach(m => m.Receive(from, message)); } public void RegisterMembers(params TeamMember[] teamMembers) { foreach (var member in teamMembers) { this.Register(member); } } public override void SendTo<T>(string from, string message) { this.members.OfType<T>().ToList().ForEach(m => m.Receive(from, message)); } }
ChatRoom1.cs
public class TeamMember1 : TeamMember { public TeamMember1(string name) : base(name) { } public override void Receive(string from, string message) { Console.Write($"{this.Name} ({nameof(TeamMember1)}) has received: "); base.Receive(from, message); } }
TeamMember1.cs
public class TeamMemer2 : TeamMember { public TeamMemer2(string name) : base(name) { } public override void Receive(string from, string message) { Console.Write($"{this.Name} ({nameof(TeamMemer2)}) has received: "); base.Receive(from, message); } }
TeamMember2.cs
class Program { static void Main(string[] args) { var teamChat = new ChatRoom1(); var steve = new TeamMember1("Steve"); var justin = new TeamMember1("Justin"); var jenna = new TeamMember1("Jenna"); var kim = new TeamMemer2("Kim"); var julia = new TeamMemer2("Julia"); teamChat.RegisterMembers(steve, justin, jenna, kim, julia); steve.Send("Hey everyone, we're going to be deploying at 2pm today."); kim.Send("Ok, thanks for letting us know."); Console.WriteLine(); steve.SendTo<TeamMember1>("Make sure to execute your unit tests before checking in!"); } }
Program.cs
As you can see in the above implementation, Concreate implementations of Team members is only defining the Receive logic. What they want to do when they receive any message. All the hard lifting is done by Mediator itself. Creating object of the Colleagues, Adding Colleagues references to private list & Setting Chat Room of the Colleagues. The above code removed the coupling b/w TeamMember1 & TeamMember2 by introducing a Mediator Class ChatRoom1.
General Code For Mediator Design Pattern
Let's also discuss the code based on general Mediator Design Pattern.
public abstract class Mediator { public abstract void Send(string message, Colleague colleague); }
Mediator.cs
public abstract class Colleague { protected Mediator mediator; //public Colleague(Mediator mediator) //{ // this.mediator = mediator; //} internal void SetMediator(Mediator mediator) { this.mediator = mediator; } public virtual void Send(string message) { this.mediator.Send(message, this); } public abstract void HandleNotification(string message); }
Colleague.cs
Now let's look at Concrete implementations of Mediator & Colleague classes.
public class ConcreteMediator : Mediator { private List<Colleague> colleagues = new List<Colleague>(); public void Register(Colleague colleague) { colleague.SetMediator(this); this.colleagues.Add(colleague); } public T CreateColleague<T>() where T : Colleague, new() { var c = new T(); c.SetMediator(this); this.colleagues.Add(c); return c; } public override void Send(string message, Colleague colleague) { this.colleagues.Where(c => c != colleague).ToList().ForEach(c => c.HandleNotification(message)); } }
ConcreteMediator.cs
public class Colleague1 : Colleague { //public Colleague1(Mediator mediator) : base(mediator) //{ //} public override void HandleNotification(string message) { Console.WriteLine($"Colleague1 receives notification message: {message}"); } }
Colleague1.cs
public class Colleague2 : Colleague { //public Colleague2(Mediator mediator) : base(mediator) //{ //} public override void HandleNotification(string message) { Console.WriteLine($"Colleague2 receives notification message: {message}"); } }
Colleague2.cs
Mediator Pattern is used majorly in broadcasting scenario, where state change in one class needs to be notified to multiple other classes & they all can notify each other of any change happened within their state.
There are several .Net libraries available which are based on this pattern, one of them is MediatR. It's not the exact same implementation as defined above, but it's useful
in slice architecture. Avoid using
MediatR library in Onion architecture or Peel based architectures.
A lot of people with existing knowledge about Observer design pattern gets confused with the Mediator design pattern & tries to find out difference b/w both as at first glance both look exactly same. But here is the Stack overflow thread that discuss about difference b/w both in detail: Mediator Vs Observer Object-Oriented Design Patterns
Thanks for reading! Have a nice day.
- Get link
- X
- Other Apps
- Get link
- X
- Other Apps
Comments
Post a Comment