Mediator design pattern basically defines an interface class which provides a mechanism for providing communication between various modules/components of the software. Since, mediator needs to access each component of the software, which means it should be added as a reference in each component. Using this mediator class components becomes less dependent on each other which means coupling is reduced.
Before going ahead have a look at Design pattern simplified version.
Generic model class diagram of Mediator design pattern is as shown in image.
This design pattern has following major components:
- Mediator – This class defined the interface APIs for communication between modules/components.
- Concrete Mediator — This class provide the implementation for APIs defined by Abstract class Mediator based on Concrete Colleagues.
- Colleague – This class defines the interface APIs for communication with other colleagues using Mediator class.
- Concrete Colleague — This class provide the implementation for APIs defined by Abstract class Colleague through respective mediators.
Mediator design pattern Code Example:
Let’s take an example of Internet chat room. Now in a typical chat room any person would be able to join any chat room of their wishes, can send private or broadcast messages.
#include <iostream>
#include <unistd.h>
#include <vector>
using namespace std;
class Chatroom;
class Person
{
public:
string name;
Chatroom* room = NULL;
Person (const string& n)
{
name = n;
}
void msg_receive (const string& sender, const string& msg);
void msg_send (const string& target, const string& msg);
void msg_broadcast (const string& msg);
};
class Chatroom
{
public:
vector <Person*> persons;
void join (Person *p)
{
string msg_join = p->name + " Joins the chatroom";
broadcast ("chatroom", msg_join);
p->room = this;
persons.push_back (p);
}
void broadcast (const string& sender, const string& msg)
{
for (auto p: persons)
{
if (p->name != sender)
p->msg_receive (sender, msg);
}
}
void message (const string& sender, const string& receiver, const string& msg)
{
for (auto p: persons)
{
if (p->name == receiver)
p->msg_receive (sender, msg);
}
}
};
void Person::msg_receive (const string& sender, const string& msg)
{
cout << name << " chat screen: " << sender << ": " << msg << endl;
}
void Person::msg_send (const string& target, const string& msg)
{
room->message (name, target, msg);
}
void Person::msg_broadcast (const string& msg)
{
room->broadcast (name, msg);
}
int main ()
{
Chatroom room;
Person im {"IronMan"};
Person ca {"Captain America"};
Person hu {"Hulk"};
room.join (&im);
room.join (&ca);
room.join (&hu);
ca.msg_broadcast ("Hello Avengers !!! Ready for END GAME");
im.msg_broadcast ("Aye Aye Captain !!!");
hu.msg_broadcast ("Aye Aye Captain !!!");
ca.msg_send ("Hulk", "Smash everyone !!!");
ca.msg_send ("IronMan", "Tony close the gate!!!");
return 0;
}
Output of above example:
IronMan chat screen: chatroom: Captain America Joins the chatroom
IronMan chat screen: chatroom: Hulk Joins the chatroom
Captain America chat screen: chatroom: Hulk Joins the chatroom
IronMan chat screen: Captain America: Hello Avengers !!! Ready for END GAME
Hulk chat screen: Captain America: Hello Avengers !!! Ready for END GAME
Captain America chat screen: IronMan: Aye Aye Captain !!!
Hulk chat screen: IronMan: Aye Aye Captain !!!
IronMan chat screen: Hulk: Aye Aye Captain !!!
Captain America chat screen: Hulk: Aye Aye Captain !!!
Hulk chat screen: Captain America: Smash everyone !!!
IronMan chat screen: Captain America: Tony close the gate!!!
Advantages
- It reduces tight coupling as interacting modules not linked to each other directly.
- One component can easily be modified without affecting other modules.
Disadvantages:
- Since, mediator class needs to understand about components in order to provide interactions which makes them complex at times.