The Mediator Design Pattern in C#

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:

  1. Mediator — Defines communication between Colleagues.
  2. Concrete Mediator — Implements communication between Colleagues.
  3. Colleague — Communicates only with the Mediator.
  4. Concrete Colleague — Receive messages from the Mediator.

Mediator.jpg

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.

Colleague_constructor.jpg

Then I implemented method Send which will send a message to that Mediator we stored into the protected field via its Send method.

Colleague_send_method.png

Finally, I have an abstract HandleNotification method at the bottom. Think about this as we need somewhere to receive messages.

Colleague_Receive.png

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.

Console.PNG

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.