The Mediator Design Pattern in C#
Enhance and simplify the object dependency graph with the Mediator Design Pattern.
Introduction
Through the self-education journey, I finally understood a Mediator. The Mediator Design Pattern can be a beneficial tool in your toolbox when you progress as a Developer.
Prerequisites
This tutorial expects you to know the basics of C# language and Object-oriented Programming.
Book Definition
A Mediator Design Pattern (Mediator) was first-time officially introduced in a book titled Design Patterns: Elements of Reusable Object-Oriented Software.
Mediator encapsulates how objects interact and communicate with each other. This pattern promotes the loose coupling of objects to avoid an ugly dependency graph.
Ugly dependency graph is precisely what you and I should try to avoid. Loose coupling is one of the ways how to do this. And Mediator is the concrete implementation of doing it.
The Mediator enables an object to avoid referring directly to each other by encapsulating communication in a central Mediator object.
Imagine a bunch of objects in your codebase. If each of the objects must relate and communicate with all other purposes, this could get ugly in a hurry.
Developer friendly explanation
With Mediator, we create a central Mediator object. This single object has the sole responsibility for maintaining references to the objects in our “bunch of objects”.
It is also responsible for relaying any messages or communication between and to these objects. You can think of Mediator of like a communication hub.
A Mediator commonly consists of these four components:
- Mediator — Defines communication between Colleagues.
- Concrete Mediator — Implements communication between Colleagues.
- Colleague — Communicates only with the Mediator.
- Concrete Colleague — Receive messages from the Mediator.
Relationship between components
The Mediator
is typically an abstract-based class. Concrete Mediator
inherits from the base Mediator and implements the communication that was primarily defined by the contract or base methods in the Mediator.
The Colleague
is an abstract-based class also but represents a related collection of objects. It references only its Mediator and communicates with it. You can think of this as bidirectional between the Mediator and Colleagues.
The Concrete Colleague
is simply a different type of subclasses that inherit from the abstract Colleague base class and defines specific behavior.
Implementation
Let’s dive into code and implement our very first Mediator. First, what we will need to create is an abstract class named Mediator and its abstract method Send.
Then create an abstract class Colleague.
As you can see, I added a little bit of implementation.
The first thing is that we have a constructor for the Colleague
that takes in a Mediator and sets it into a protected field.
Then I implemented method Send
which will send a message to that Mediator we stored into the protected field via its Send method.
Finally, I have an abstract HandleNotification
method at the bottom. Think about this as we need somewhere to receive messages.
Now Let’s add Concrete Colleagues that inherit from this abstract Colleague
class.
While I inherit from abstract class Colleague
, I implemented the method HandleNotification
. Implementation is a simple printing of notification of received message into the console.
The last class needs to be defined, and it is a ConcreteMediator
.
Look at the Send
method. As you can see, Mediator is handling communication between objects. In this concrete situation, I am passing the message from Colleague1
to Colleague2
and conversely.
Now let’s go into Program.cs to use all this.
Let’s instantiate the ConcreteMediator
. Then I need instances of both Colleagues as objects c1
and c2
. Notice that I am passing the Mediator
into both. Afterward, we must also set both of Mediator’s properties and by that is defined already mentioned bidirectional references. Now we can’t start to send a message. Build and run the code.
We can see that messages were delivered and received by Colleagues
to each other.
Conclusion
The mediator is a communication hub between objects. It resolves the correct message direction for you. One of the main benefits of the Mediator is the ability to encapsulate object interaction with loose coupling between objects. Remember, patterns are just guiding rails, not prescriptive implementation rules that are set in stone. Many different implementations are acceptable.