design pattern

Behavioral Design Patterns: Mediator

Making our way through the detailed Guide to Software Design Patterns series we’ve been publishing, today we’ll be exploring the handy mediator design pattern. In the mediator design pattern, a mediator object encapsulates how other types of objects interact, without the need for those objects to be aware the implementation of other objects.

Throughout this article we’ll investigate the mediator design pattern using a real world example. We’ll follow that up with a fully-functional C# code sample that will illustrate how the mediator pattern can be quickly and easily implemented, so let’s get to it!

In the Real World

The mediator design pattern obviously originates from the concept of mediation, or to mediate. Its purpose is to serve as a go-between, so other components can properly interact with one another, even in situations where doing so may be difficult. In fact, the obvious real world example of a mediator pattern is in a scenario like group counseling. A therapist may act as a mediator between spouses who are going through couples therapy. Or, a mediator may drive discussion and encourage speakers at a local AA meeting. Lawyer jokes aside, attorneys often act as mediators during difficult proceedings between family members or friends who find themselves in a difficult legal battle.

Outside the realm of the humanities, most of us experience some form of technological mediation multiple times a day. Virtually every modern communication app on our phones uses some form of the mediator pattern to connect multiple phones (and, thereby, people) together. When you send a text to your BFF, you aren’t making a direct connection from your phone to your friend’s phone. Instead, your carrier receives your request and establishes a connection on your behalf, acting as the mediator. The bits that makeup your message are sent out to the nearby cell tower to a switching service handled by your carrier. The carrier then passes the message along via the fastest fiber optic route to the cell tower nearest to your friend. From there, the message finally arrives at your friend’s phone. In some cases, satellites might even be involved, which also behave as a mediator in many ways.

How It Works In Code

The mediator pattern has three major components:

  • Mediator – Coordinates communication between all colleague objects.
  • Colleague – Sends message to and receives messages from other colleagues through an associated mediator object.
  • Client – Creates colleagues and associates an appropriate mediator with each.

Having just watched the latest episode of Game of Thrones, it seems appropriate to tie the show into our code sample in some way. To do so, we’ll loosely base our code on the real world telephonic communication example cited above — a carrier service behaves as a mediator so two callers (colleagues) can communicate with one another. However, cell reception is spotty, at best, way up north on The Wall, so our sample code uses a “town crier” (Crier class) to act as the go-between or mediator for our list of people who want to communicate with each other. When a character says something, the crier hears the message and shouts it out, ensuring that all the other characters hear the message themselves and know who said it.

The full code sample can be found below, after which we’ll break it down into smaller chunks to see how the mediator pattern is actually working:

We start by defining the ICrier interface and the Crier class that implements it:

The Crier class acts as our mediator object and must receive the colleague object that is trying to send a message (Person, in this case). The Shout(string message, Person source) method handles the only logic we need for mediation. Specifically, we’re using the MessageReceived event handler — a delegate with the same signature as the calling method — to fire when the crier shouts out a message.

Now we need to define at least one colleague object. This will be the IPerson interface and the Person class that implements it:

The first requirement of the Person class is that it has an association with a Crier (mediator). We accomplish this through the constructor parameter and the Crier property assignment. The other requirement is the ability for Person to send a message (or some other behavior) that can be handled by the associated mediator. In this case, the Say(string message) method handles that by calling Crier.Shout(message, this). In the fantasy scenario we’ve built for ourselves, this means that when a person says something, the crier hears it and shouts it loudly, relaying not only the message, but also the person it originated from.

Since we want to avoid having to directly associate every single unique instance of our colleague (Person) object with every other, we’re using an event/subscriber pattern so all persons can listen for messages shouted by the crier. In the constructor we subscribe the Listen(string message, Person source) method to the Crier.MessageReceived handler. Thus, every time the Crier shouts out a message, each Person will receive that subscribed event, effectively allowing everyone to listen to what every else says.

Alright, enough messing about with setup, let’s see this in action. Our client code uses the Crier and Person classes within the Program.Main() method:

Connoisseurs of the books (or even the show): please bear with me. I wanted to choose characters that had some interesting quotes, so we’ll need to pretend that these characters were all hanging out together, perhaps at a lovely gala, when these particular utterances were made.

As you can see, we start by instantiating a Crier object. Then, we create a series of characters and be sure to pass the crier instance to each constructor, which forms the fundamental connection between Colleague and Mediator that the entire mediator pattern relies on.

Now our characters can Say() whatever they’d like. By using a mediator design pattern here, the goal is to allow anything said by one character to be heard by all other characters, as relayed by the associated crier. To see if everything works as expected, let’s take a look at the output produced by the code above:

As expected, each message sent by an individual is transferred to everyone else via the crier. For example, we see that when Arya says “Valar morghulis”, all the other characters receive that message and know it is from her, even though no two Person instances have a connection or awareness of one another. It’s also worth noting that, because of the use of an event handler, even though Ilyn Payne cannot speak, he can still listen and is able to receive all the messages sent by the others.

That should do it for now! Hopefully this gave you a better sense of what the mediator design pattern is for and how it can be relatively easily implemented into almost any scenario. For more information on all the other popular design patterns, head on over to our ongoing design pattern series here!