Mediator Design Pattern In .NET C#
top of page

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

  • Writer's pictureAhmed Tarek

Mediator Design Pattern In .NET C#

Updated: Apr 17

Learn about the Mediator Design Pattern in .NET C# with code samples.


The Mediator Design Pattern in DotNet (.NET) CSharp (C#) with code samples Mediatr Software Architecture Engineering Development Coding Programming Best Practice Enhancement
Photo by Jonathan on Unsplash, adjusted by Ahmed Tarek

Mediator Design Pattern Definition


The Mediator Design Pattern is one of the behavioral design patterns.


According to Wikipedia, the definition is as follows:

The essence of the Mediator Pattern is to “define an object that encapsulates how a set of objects interact”. It promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to be varied independently. Client classes can use the mediator to send messages to other clients, and can receive messages from other clients via an event on the mediator class.

Therefore, to put it in simple words, the Mediator Design Pattern is used to manage the interactions between group of objects (similar or not) keeping the the objects isolated from each other.


To understand this, let me explain more with a simple example.


Let’s say that you are implementing a Chat Room. So, you have unlimited number of users who can join the room and once any user sends a message, the message should be received by all other users on the Chat Room.


So, implementing this, you would have a User class with SendMessage method.


However, you still need to have access to the other Users so that you can direct your the message to them. Also, whenever, a User leaves the Chat Room, you would also need to remove this User from all other Users so that they don’t send a message to him.


You have more than one way to achieve this:

  1. Defining a list of Users inside each User and keeping these lists updated whenever a User enters or leaves the Chat Room.

  2. Using another class or module to act as the middle man to handle the Users list and the way of sending and receiving the messages between different Users.


The first option would work, but, it is not good because:

  1. The User class would be doing too many things which are not actually related to its main job.

  2. Every User now knows about the other users which is not logical.

  3. If we want to apply some business rules on which User is allowed to communicate with which User, it would be so hard.

  4. Too much processing for maintaining all lists of Users.


Now, if we explore the second option:

  1. The User class does its job and only job.

  2. All the logic of maintaining the Users list is centralized into one class or module.

  3. This enables us to easily apply business rules and aggregations if needed.


As you can see, it would be better to go with the second option. Now, let me tell you that what you are going to implement in the second option is the Mediator Design Pattern.


 


 

Mediator Design Pattern Class Diagram


What I noticed about the Mediator Design Pattern is that it could be implemented into more than one flavor.


Sometimes you could use Events, Delegates, or Direct Methods. Sometimes you could use the Mediator in many places and inject it as a dependency, or you can use it only in a centralized manager.


Therefore, this made me see the Mediator Design Pattern as a concept more than a pattern.


However, to help you visualize how the Mediator Design Pattern could be, here is a class diagram from Wikipedia.


The Mediator Design Pattern in DotNet (.NET) CSharp (C#) with code samples Mediatr Software Architecture Engineering Development Coding Programming Best Practice Enhancement
The mediator behavioral design pattern, adjusted by Ahmed Tarek

 

The Mediator Design Pattern in DotNet (.NET) CSharp (C#) with code samples Mediatr Software Architecture Engineering Development Coding Programming Best Practice Enhancement
Photo by Arnold Francisca on Unsplash, adjusted by Ahmed Tarek

The Code Example


Now its time to dive in some code to understand the Mediator Design Pattern. What we are going to do now is to implement the Chat Room example.


Let’s start with the User class.



What we can notice here:

  1. We defined MessageBroadcastedEventHandler delegate to be used as the event fired by each User when it is sending a message.

  2. Then it is obvious that we defined event MessageBroadcastedEventHandler MessageBroadcasted in the User class.

  3. We also defined protected void OnMessageBroadcasted(string message) as a best practice for handling firing the event.

  4. Each User has a Name property.

  5. Also, each User has SendMessage method to be called by the main module when a certain User is sending a message. Here we are just firing the event through OnMessageBroadcasted.

  6. The ReceiveMessage method is used to inform a User when a message is broadcasted. Here we are just logging some information for testing.


Now, let’s move to the ChatRoom class which is our Mediator class.



What we can notice here:

  1. We defined a private list of Users.

  2. We defined an AddUser method to be called when adding a new User to the ChatRoom. What we do here is that we add the user to the private list and subscribe to its MessageBroadcasted event.

  3. We defined a RemoveUser method to be called when removing a User from the ChatRoom. What we do here is that we remove the user from the private list and unsubscribe from its MessageBroadcasted event.

  4. We defined OnMessageBroadcasted method to handle the MessageBroadcasted event of each User. What we do here is that we loop on all Users except for the one broadcasting the message and trigger their ReceiveMessage method.


Moving to the main module.



What we can notice here:

  1. We are defining 3 Users.

  2. We created the Chat Room and added the 3 Users one by one.

  3. Then, Ahmed started by saying “Hi all.”. We got this in the console:

  4. Then Tarek replied by saying “Hi Ahmed.”. We got this in the console:

  5. Then Tarek is removed from the Chat Rom.

  6. Then Tarek sent a message saying “Anybody can hear me?”. However, we didn’t get anything in the console because Tarek was already removed from the Chat Room.

  7. Then Hasan sent a message saying “I think Tarek is disconnected.”. We got this in the console:


So, now everything is working as it should and the important thing is that our objects (in our case; Users) are loosely coupled.


Additionally, if we want to apply some business rules we can easily do it because we have the relations and interactions centralized into one place. This also makes it so easy to apply aggregations.


 


 

The Mediator Design Pattern in DotNet (.NET) CSharp (C#) with code samples Mediatr Software Architecture Engineering Development Coding Programming Best Practice Enhancement
Photo by Sammy Williams on Unsplash, adjusted by Ahmed Tarek

The Misconception


There is a misconception that the Mediator Design Pattern manages the relation and interaction between similar objects or objects of the same type, however, this is not true.


The Mediator Design Pattern can do the same job with different kinds of objects. One of the famous examples is the Request-Handler pattern implementation.


In simple words, if you have some input or request which would be channeled through a series of different handlers (may be predefined or loaded at run-time), the Mediator Design Pattern would be great to use.


The main benefit you would get by using the Mediator Design Pattern is that you would achieve a loosely coupled design so that the request and the handlers are not aware of each other.


Let’s see an example.


 

Request-Handler Example


Let’s say that we have an Employee management system and we have different actions to execute whenever a new employee is hired.


In this case, you don’t want to run or trigger these actions all in one place like the CreateEmployee method in the business layer. It would work, but, you would end up with a bloated business layer. Also, every time you want to add a new action, you would go and modify the CreateEmployee method. This is not good as you might break something when you didn’t mean to.


Therefore, using the Request-Handler pattern here would be good.


Note: For simplicity, some best practices would be dropped here to drive the focus on the main idea of the Request-Handler pattern.



What we can notice here:

  1. We defined a simple Employee class.

  2. We defined a simple IAddEmployeeRequest interface. It has only one property of an Employee to be added.

  3. We defined a simple AddEmployeeRequest class implementing the IAddEmployeeRequest interface.

  4. We defined the IAddEmployeeRequestHandler interface to represent any handler to the IAddEmployeeRequest request.

  5. We have two simple implementations of the IAddEmployeeRequestHandler interface; AddEmployeeRequestHandler1 and AddEmployeeRequestHandler2.



What we can notice here:

  1. We defined the AddEmployeeMediator class.

  2. It maintains a list of IAddEmployeeRequestHandler handlers.

  3. When a request comes in through the Handle method, it loops on all the registered handlers and trigger their Handle method.

  4. The RegisterHandler and UnregisterHandler are two methods for registering and unregistering handlers.



Running this, we would get the following result:

AddEmployeeRequestHandler1 handled Ahmed
AddEmployeeRequestHandler2 handled Ahmed
AddEmployeeRequestHandler1 handled Tarek
AddEmployeeRequestHandler2 handled Tarek

This is great. Now we have our requests handled by different handlers without having tightly coupled requests and handlers.


For sure the sample code is not in the best shape and it could be enhanced by providing more abstraction and doing some refactoring, but this is something you can easily do, right?


Before leaving this step on our trip, let me tell you that one of the famous libraries implementing this concept is MediatR.


 


 

The Mediator Design Pattern in DotNet (.NET) CSharp (C#) with code samples Mediatr Software Architecture Engineering Development Coding Programming Best Practice Enhancement
Photo by Ben White on Unsplash, adjusted by Ahmed Tarek

What is MediatR?


MediatR, by the team’s definition, and I quote, is:

Simple, unambitious mediator implementation in .NET
In-process messaging with no dependencies.
Supports request/response, commands, queries, notifications and events, synchronous and async with intelligent dispatching via C# generic variance.

If you want you can check some examples of using MediatR on this wiki page.


Nowadays it has been heavily used with .NetCore and it is known to be used while implementing Command and Query Responsibility Segregation (CQRS).


I really recommend that you give it a try.


The Mediator Design Pattern in DotNet (.NET) CSharp (C#) with code samples Mediatr Software Architecture Engineering Development Coding Programming Best Practice Enhancement
Photo by ColiN00B on Pixabay, adjusted by Ahmed Tarek

Final Thoughts


The Mediator Design Pattern is one of the greatest patterns ever. At some point, I figured out that I have been using it before even knowing about it.


Sometimes it is too obvious when to use this pattern and some other times it gets a little bit tricky.


To know more about the Mediator Design Pattern, I recommend to not just get satisfied by what you learnt on this article and keep doing some research and trials. This would help you understand it more.



Recent Posts

See All

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

bottom of page
Mastodon Mastodon