 .NET 2.0+Mediator Design PatternThe mediator pattern is a design pattern that promotes loose coupling of objects by removing the need for classes to communicate with each other directly. Instead, mediator objects are used to encapsulate and centralise the interactions between classes.
What is the Mediator Pattern?
The mediator pattern is a Gang of Four design pattern. This is a behavioural pattern as it defines a manner for controlling communication between classes or entities. The mediator pattern is used to reduce coupling between classes that communicate with each other. Instead of classes communicating directly, and thus requiring knowledge of their implementation, the classes send messages to a mediator object. The mediator object then transmits the messages to the other classes in a manner that they can interpret. The object initiating a message therefore requires no knowledge of the objects that will receive it.
The mediator pattern promotes loose coupling of classes by removing the direct dependencies. It can also simplify communication in general when a program contains a large number of classes that interact. Each class need only know how to pass messages to its mediator, rather than to numerous colleagues. This simplified communication can improve the readability of the code. It can also increase the maintainability, as a class and its mediator may be changed without requiring modifications to other types.
An example of the mediator design pattern could be used when developing an on-line presentation tool that connects a presenter to multiple attendees. When the presenter updates the on-screen slide, the new picture would be sent to all attendees. Attendees may be able to send a question that the presenter will receive and answer. Each of these actions would be sent to a method of the mediator. The mediator would then pass the messages on to the correct recipients. The objects representing the person that initiated the communication would be unaware of the number of other attendees.
Implementing the Mediator Pattern

The UML class diagram above shows an implementation of the mediator design pattern. The items in the diagram are described below:
- ColleagueBase. This abstract class is the base class for the colleague object. It defines a single, protected field that holds a reference to a mediator.
- ConcreteColleague A/B. The concrete colleagues are the classes that communicate with each other via the mediator. Each inherits the mediator field from the ColleagueBase class. The Send and Receive methods in the UML diagram are used to send messages to and receive messages from the mediator.
- MediatorBase. This abstract class is the base class for the concrete mediator objects. The class defines methods that can be called by colleague objects.
- ConcreteMediator. The concrete mediators implement the communication methods of the MediatorBase class. They also hold references to the colleagues that they are currently servicing. The SendMessage method in the UML diagram is called by a colleague object when it wished to transmit a message, passing the caller object as a parameter. The mediator then decides which of the colleagues should receive the message and calls their Receive methods.
The following shows the basic code of the mediator design pattern implemented using C#. To simplify the example the code uses C# 3.0 automatically implemented property syntax to define properties. For earlier versions of the language you should use full property declarations with backing variables.
public abstract class ColleagueBase
{
protected MediatorBase _mediator;
public ColleagueBase(MediatorBase mediator)
{
_mediator = mediator;
}
}
public class ColleagueA : ColleagueBase
{
public ColleagueA(MediatorBase mediator) : base(mediator) { }
public void Send()
{
Console.WriteLine("ColleagueA sent message");
_mediator.SendMessage(this);
}
public void Receive()
{
Console.WriteLine("ColleagueA received message");
}
}
public class ColleagueB : ColleagueBase
{
public ColleagueB(MediatorBase mediator) : base(mediator) { }
public void Send()
{
Console.WriteLine("ColleagueB sent message");
_mediator.SendMessage(this);
}
public void Receive()
{
Console.WriteLine("ColleagueB received message");
}
}
public abstract class MediatorBase
{
public abstract void SendMessage(ColleagueBase caller);
}
public class ConcreteMediator : MediatorBase
{
public ColleagueA Colleague1 { get; set; }
public ColleagueB Colleague2 { get; set; }
public override void SendMessage(ColleagueBase caller)
{
if (caller == Colleague1)
{
Colleague2.Receive();
}
else
{
Colleague1.Receive();
}
}
}
Example Mediator
In the opening section of the article an example mediator was described. In this example the colleagues are a presenter, who is giving a demonstration, and a variable number of attendees, who are watching the presentation. The mediator holds references to a single presenter and multiple attendee objects with the latter held in a list. The attendees may ask questions. The presenter may answer questions and send new images to the attendees.
The code for the example is as follows. You should create this in a console application as the actions are outputted to the console. Each communication's output is shown using a different console colour for readability.
public abstract class PresentationMember
{
protected Mediator _mediator;
public PresentationMember(Mediator mediator)
{
_mediator = mediator;
}
public string Name { get; set; }
public void ReceiveAnswer(string answer)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("{0} received anwer.\n'{1}'.", Name, answer);
}
}
public class Presenter : PresentationMember
{
public Presenter(Mediator mediator) : base(mediator) { }
public void SendNewImageUrl(string url)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Presenter changed image URL to '{0}'.", url);
_mediator.UpdateImage(url);
}
public void ReceiveQuestion(string question, Attendee attendee)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Presenter received question from {0}.\n'{1}'"
, attendee.Name, question);
}
public void AnswerQuestion(string answer, Attendee attendee)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("Presenter answered question for {0}.\n'{1}'"
, attendee.Name, answer);
_mediator.SendAnswer(answer, attendee);
}
}
public class Attendee : PresentationMember
{
public Attendee(Mediator mediator) : base(mediator) { }
public void AskQuestion(string question)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("{0} asked question.\n'{1}'", Name, question);
_mediator.SendQuestion(question, this);
}
public void ReceiveImage(string url)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Image for {0} updated to '{1}'.", Name, url);
}
}
public class Mediator
{
public Presenter Presenter { get; set; }
public List<Attendee> Attendees { get; set; }
public void UpdateImage(string url)
{
foreach (Attendee attendee in Attendees)
{
attendee.ReceiveImage(url);
}
}
public void SendAnswer(string answer, Attendee attendee)
{
attendee.ReceiveAnswer(answer);
}
public void SendQuestion(string question, Attendee attendee)
{
Presenter.ReceiveQuestion(question, attendee);
}
}
Testing the Mediator
To test the mediator we can use each of the methods available to the presenter and the attendees. Add the following code to the Main method of the program and execute it to see the results.
Mediator mediator = new Mediator();
Presenter presenter = new Presenter(mediator);
presenter.Name = "Bob";
mediator.Presenter = presenter;
Attendee sam = new Attendee(mediator);
sam.Name = "Sam";
Attendee jim = new Attendee(mediator);
jim.Name = "Jim";
mediator.Attendees = new List<Attendee> { sam, jim };
presenter.SendNewImageUrl("Slide1.jpg");
sam.AskQuestion("How often should I do this?");
presenter.AnswerQuestion("Daily", sam);
presenter.SendNewImageUrl("Slide2.jpg");
/* OUTPUT
Presenter changed image URL to 'Slide1.jpg'.
Image for Sam updated to 'Slide1.jpg'.
Image for Jim updated to 'Slide1.jpg'.
Sam asked question.
'How often should I do this?'
Presenter received question from Sam.
'How often should I do this?'
Presenter answered question for Sam.
'Daily'
Sam received anwer.
'Daily'.
Presenter changed image URL to 'Slide2.jpg'.
Image for Sam updated to 'Slide2.jpg'.
Image for Jim updated to 'Slide2.jpg'.
*/
|