Soon we will go to a specific Mediator discussion, only a small news, after the publication of this entry may not be another entry for a month, two months or more, because I would like to deal more with the translation of this blog so that not only Polish had access to it and I would like to improve its functionality and some other entries and of course I also have other projects and I would like a little rest 😓 , so it may take a while, the blog treats temporarily as an add-on, as I said I have other projects, but it should change in the future because I like to help others. Ok, let’s get to the facts 🙂
The Mediator pattern gives a uniform interface for sending messages between classes, this interface contains all references to these classes, i.e. it simply reduces the number of dependencies between them, if for example we need a dependency or element from another class, we send a command to the mediator interface, and this returns us appropriate value.
If we wanted to write dependencies in the traditional way, i.e. simply to stuff dependencies to classes that depend on these dependencies, then there is a tangle of dependencies in these classes let us assume that we have group of three people who are administers, devs and users, the dependencies look like this as below in the picture.
A tangle of dependencies emerges from such a traditional operation, it is unacceptable in larger projects. And if we used the Mediator pattern then we would have something like this:
And you can see that it not only looks better and is much clearer by that arrangement objects do not know about themselves and the less objects know about themselves the better, there is less probability of error and if they want to get some dependence from another class they just send command to the mediator interface and this one returns the appropriate value.
It is best to use the mediator’s pattern in situations where we have many dependencies between classes, and so-called spaghetti code is created, which is difficult to understand 🙂 . It’s best to learn as fast as you can how to write a non-spaghetti code. 😐
That’s a bit of it is, I’m aware of it, that’s why I’ll tell prank, so that I and you will also rest. 🙂😂
The patient comes to the doctor and asks:
– Doctor, how long will I live !?
-Hmm, are you drinking?
-No! I never drank !!
– Have you smoke?
-No! Also never!
-Some women did you have?
– No! So, Doctor, how long will I live ?!
– Hundred years I think … but why the fk like that?**
Well, the end of the prank 😂, let’s move on 🙂
The general UML diagram of the Mediator pattern looks like this:
It shows that the classes communicate through the Mediator class, but usually the detailed implementation of the Mediator pattern looks like in the diagram below:
In the diagram above, the classes inheriting from the Widget class, ie Table, Tree, CheckBox, use the Intermediary class, which is the equivalent of the Mediator class from the first diagram.
In the code, the mediator pattern looks more or less like the one below. As usual, in the structure area gives a simple example to make it easy to understand, let’s start with the Mediator class:
namespace MediatorSchema
{
abstract class Mediator
{
public abstract void Send(string message, Colleague colleague);
}
class ConcreteMediator : Mediator
{
private Colleague _colleague1;
private Colleague _colleague2;
public Colleague Colleague1
{
set { _colleague1 = value; }
}
public Colleague Colleague2
{
set { _colleague2 = value; }
}
public override void Send(string message, Colleague colleague)
{
if (colleague == _colleague1)
{
_colleague2.Notify(message);
}
else
{
_colleague1.Notify(message);
}
}
}
}
We see that arguments are sent to the Send() method and, depending on the type of class passed, pass the message variable to the appropriate class. At the top, we have the Colleague type set twice, so that we do not create a specific class type, and we pass concrete objects to the ConcreteMediator class in the client.
namespace MediatorSchema
{
class Program
{
static void Main(string[] args)
{
ConcreteMediator m = new ConcreteMediator();
ConcreteColleague1 c1 = new ConcreteColleague1(m);
ConcreteColleague2 c2 = new ConcreteColleague2(m);
m.Colleague1 = c1;
m.Colleague2 = c2;
c1.Send("How are you?");
c2.Send("Fine, thanks");
Console.ReadKey();
}
}
}
And also ConcreteColleague1 and ConcreteColleague2 classes.
{
abstract class Colleague
{
protected Mediator mediator;
public Colleague(Mediator mediator)
{
this.mediator = mediator;
}
public abstract void Send(string message);
public abstract void Notify(string message);
}
class ConcreteColleague1 : Colleague
{
public ConcreteColleague1(Mediator mediator) : base(mediator) { }
public override void Send(string message)
{
mediator.Send(message, this);
}
public override void Notify(string message)
{
Console.WriteLine("Colleague1 gets message: " + message);
}
}
class ConcreteColleague2 : Colleague
{
public ConcreteColleague2(Mediator mediator) : base(mediator) { }
public override void Send(string message)
{
mediator.Send(message, this);
}
public override void Notify(string message)
{
Console.WriteLine("Colleague2 gets message: " + message);
}
}
}
When we return to the client, Send() methods are called in the ConcreteColleague1 and ConcreteColleague2 classes, which only forward the message to the ConcreteMediator class and depending which class invoked the Send() method, the corresponding message from the Notify() method is displayed.
Result:
The first such example will be chat, for example, if we were to divide the code into classes according to each chat user, and for each of them to assign dependencies, the dependencies between them would be very tangled, and in this example it is good to use a mediator if a person sends a message, she is transferred to the mediator’s class and then passed to the appropriate person who was to receive the message. And all dependencies we have in one class, beautifully simple, clear and effective.
Let’s see how it looks in the code, let’s start with the mediator class.
namespace ChatRoom
{
abstract class AbstractChatroom
{
public abstract void Register(Participant participant);
public abstract void Send(string from, string to, string message);
}
class Chatroom : AbstractChatroom
{
private Dictionary<string, Participant> _participants = new Dictionary<string, Participant>();
public override void Register(Participant participant)
{
if (!_participants.ContainsValue(participant))
{
_participants[participant.Name] = participant;
}
participant.Chatroom = this;
}
public override void Send(string from, string to, string message)
{
Participant participant = _participants[to];
if (participant != null)
{
participant.Receive(from, message);
participant.Notify(from);
}
}
}
}
The principle of the mediator’s class is the same as in the previous example only the implementation is different, here we do not assign on stiffly the dependencies only with the Register() method. We register dependencies on a regular basis, check if there is such a dependency in the class, if not, we write it to the dictionary.
And in the Send() method we check if there is a registered person with name, which we passed to the Send() method in the chat, if so we send a message to it.
Let’s see what the Participant class looks like, which we defined in the dictionary.
namespace ChatRoom
{
abstract class Participant
{
private string _name;
public string Name
{
get { return _name; }
}
private AbstractChatroom _chatroom;
public AbstractChatroom Chatroom
{
set { _chatroom = value; }
get { return _chatroom; }
}
public Participant(string name)
{
_name = name;
}
public void Send(string to, string message)
{
_chatroom.Send(_name, to, message);
}
public abstract void Receive(string from, string message);
public abstract void Notify(string from);
}
}
The most important thing in this class is that we write down the name of the participant in it and forward the message to the Send() method in the Participant class and then further forward the message to the Send() method in the Chatroom class, and we have the Receive() and Notify() methods defined.
As I divided participants according to their gender, we have separate classes for them.
namespace ChatRoom
{
class Female : Participant
{
public Female(string name) : base(name) { }
public override void Receive(string from, string message)
{
Console.WriteLine("{0} to {1}: '{2}'", from, Name, message);
}
public override void Notify(string from)
{
Console.WriteLine("Woman " + Name + " gets message from: " + from);
}
}
class Male : Participant
{
public Male(string name) : base(name) { }
public override void Receive(string from, string message)
{
Console.WriteLine("{0} to {1}: '{2}'", from, Name, message);
}
public override void Notify(string from)
{
Console.WriteLine("Man " + Name + " gets message from: " + from);
}
}
}
And in these classes we have defined Receive() and Notify() methods.
Finally, the customer.
namespace ChatRoom
{
class Program
{
static void Main(string[] args)
{
Chatroom chatroom = new Chatroom();
Participant George = new Male("George");
Participant Jasmine = new Female("Jasmine");
Participant Ringo = new Male("Ringo");
Participant John = new Male("John");
Participant Paul = new Male("Paul");
chatroom.Register(George);
chatroom.Register(Paul);
chatroom.Register(Ringo);
chatroom.Register(John);
chatroom.Register(Jasmine);
Jasmine.Send("John", "Hi John!");
George.Send("Ringo", "Hi Ringo! How are you!");
Ringo.Send("George", "Hi George!");
Paul.Send("John", "Hi everyone!");
John.Send("George", "My homies in one place!!");
Console.ReadKey();
}
}
}
And you can see that in the client we register chat users and send messages.
Result:
Another example is the flight control tower, I bet that the software uses Mediator 🙂 there, this tower controls the flight of every plane, helicopter, etc. This is well illustrated by the picture below.
Let’s start as in the previous example about the chat from the mediator class.
namespace ControlFlight
{
abstract class TowerMediator
{
public abstract void Register(Machine machine);
public abstract void SendControlMessage(string typeMachine);
}
class ControlTower : TowerMediator
{
private Dictionary<string, Machine> _machines = new Dictionary<string, Machine>();
public override void Register(Machine machine)
{
if (!_machines.ContainsValue(machine))
{
_machines[machine.TypeMachine] = machine;
}
machine.Controltower = this;
}
public override void SendControlMessage(string typeMachine)
{
Machine machine = _machines[typeMachine];
if (machine != null)
{
Console.WriteLine("Control the flight of a {0}", machine.TypeMachine);
}
}
}
}
The principle of operation and implementation, identical to the chat example, the Register() method registers the dependencies and the SendControlMessage() method checks whether the transferred machine exists in the dictionary, if so we display the corresponding message.
Let’s see how the Machine class looks like.
namespace ControlFlight
{
abstract class Machine
{
protected string _typemachine;
public string TypeMachine
{
get { return _typemachine; }
}
protected TowerMediator _controltower;
public TowerMediator Controltower
{
set { _controltower = value; }
get { return _controltower; }
}
public Machine(string typemachine)
{
_typemachine = typemachine;
}
public abstract void SendControlMessage();
}
}
Very similar to the Participant class, only the SendControlMessage() method is defined here, it is implemented in the classes responsible for the machine types that are below.
namespace ControlFlight
{
class SailPlane : Machine
{
public SailPlane() : base("Sailplane")
{ }
public override void SendControlMessage()
{
Console.WriteLine("SailPlane Configuration");
_controltower.SendControlMessage(_typemachine);
}
}
class Hunter : Machine
{
public Hunter() : base("Hunter")
{ }
public override void SendControlMessage()
{
Console.WriteLine("Hunter Configuration");
_controltower.SendControlMessage(_typemachine);
}
}
class Helicopter : Machine
{
public Helicopter() : base("Helicopter")
{ }
public override void SendControlMessage()
{
Console.WriteLine("Helicopter Configuration");
_controltower.SendControlMessage(_typemachine);
}
}
}
It can be seen that in these classes we call the SendControlMessage() method defined in the ControlTower class to which we pass the name of the machine and check in it whether such a machine is registered.
Result
That’s all about Mediator 🙂.
Link to github with the whole code from this article: https://github.com/Slaw145/MediatorTutorial
This content also you can find on my blog http://devman.pl/programtech/design-patterns-mediator/
If you recognise it as useful, share it with others so that others can also use it.
Leave upvote and follow and wait for next articles :) .
In the next article, we will talk about the Template Method pattern.
And NECESSERILY join the DevmanCommunity community on fb, part of the community is in one place 🙂
– site on fb: Devman.pl-Sławomir Kowalski
– group on fb: DevmanCommunity
Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂.
Illustrations, pictures and diagrams are from: https://sourcemaking.com/design_patterns/mediator