The Mediator design pattern allows to unlink objects from each other.
It's like a manager that centralizes every action performed by a component in order to dispatch these events to all other objects that need to know information.
It could be seen as a design pattern quite accessible in term of complexity.
Let's try it in an example.
First of all
What's interesting in this design pattern is that each Ship doesn't know who are other Ships.
A Ship knows only its manager (the Mediator).
Then this Mediator will interact with other Ships to communicate a message from a particular Ship.
In our tutorial the Scout ship will send a message, with GPS coordinates of an ore mine found, to its Mediator.
Then this Mediator will forward this message to Extractor ships.
The main advantage of this mechanism is that a Ship can be changed completely without impacting other Ships nor the Mediator.
In this Mediator design pattern there are two main phases:
- Registration;
- Communication.
Registration
First, all Ships have to register to the Mediator.
The Mediator will then create a list of those Ships.
So only the Mediator knows who is in the list of Ships.
In our example, the list is made of Extractor ships.
Those Extractors can of course send back a message to their Mediator but in our example they will only send a message on stdout.
Communication
After the registration, a Ship (the Scout) can communicate with its Mediator to inform it that a new information is available.
Then the Mediator will spread this information to all Ships in the list.
Difference with the Observer design pattern
The Mediator is close to the Observer.
The difference is that, in the Observer, registereds don't have the right to send message to the subject.
The communication is only from the subject (a kind of manager) to the registereds, they can only receive messages.
Code
IMediator.h
#ifndef __BADPROG_IMEDIATOR_H__
#define __BADPROG_IMEDIATOR_H__
#include <string>
#include <vector>
#include <memory>
#include <iostream>
#include "IShip.h"
// Badprog.com
class IShip;
class IMediator
{
public:
virtual ~IMediator() { std::cout << __FUNCTION__ << std::endl; }; // = 0;
virtual void spreadMessage(IShip::ENUM_ORE ore, double latitude, double longitude) = 0;
virtual void addNewRegistered(std::unique_ptr<IShip> registered) = 0;
virtual const std::vector<std::unique_ptr<IShip>> &getVectorOfExtractors() const = 0;
protected:
std::vector<std::unique_ptr<IShip>> _vectorOfExtractors;
};
#endif // __BADPROG_IMEDIATOR_H__
IShip.h
#ifndef __BADPROG_ISHIP_H__
#define __BADPROG_ISHIP_H__
#include <memory>
#include <string>
#include <iostream>
#include <vector>
// Badprog.com
class IMediator;
class IShip
{
public:
enum ENUM_ORE { ORE_SILVER, ORE_GOLD };
public:
virtual ~IShip() { std::cout << __FUNCTION__ << std::endl; };
virtual void sendMessage(IMediator &mediator, const ENUM_ORE &ore, double latitude, double longitude) = 0;
virtual void receiveMessage(IShip::ENUM_ORE ore, double latitude, double longitude) = 0;
virtual const std::string &getName() const = 0;
private:
};
#endif // __BADPROG_ISHIP_H__
Mediator.cpp
#include <vector>
#include "Mediator.h"
#include "IShip.h"
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
Mediator::Mediator() {
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
Mediator::~Mediator() {
std::cout << __FUNCTION__ << " byebye I was the Mediator." << std::endl;
}
//-----------------------------------------------------------------------------
// spreadMessage
//-----------------------------------------------------------------------------
void Mediator::spreadMessage(IShip::ENUM_ORE ore, double latitude, double longitude) {
for (auto const &element : getVectorOfExtractors()) {
element->receiveMessage(ore, latitude, longitude);
}
}
//-----------------------------------------------------------------------------
// addNewRegistered
//-----------------------------------------------------------------------------
void Mediator::addNewRegistered(std::unique_ptr<IShip>registered) {
_vectorOfExtractors.push_back(std::move(registered));
}
//-----------------------------------------------------------------------------
// getVectorOfShips
//-----------------------------------------------------------------------------
const std::vector<std::unique_ptr<IShip>> &Mediator::getVectorOfExtractors() const {
return IMediator::_vectorOfExtractors;
}
Mediator.h
#ifndef __BADPROG_MEDIATOR_H__
#define __BADPROG_MEDIATOR_H__
#include <iostream>
#include <string>
#include <list>
#include <memory>
#include "IMediator.h"
#include "IShip.h"
// Badprog.com
class IShip;
class Mediator : public IMediator
{
public:
Mediator();
virtual ~Mediator();
virtual void spreadMessage(IShip::ENUM_ORE ore, double latitude, double longitude) override;
virtual void addNewRegistered(std::unique_ptr<IShip> registered) override;
virtual const std::vector<std::unique_ptr<IShip>> &getVectorOfExtractors() const override;
};
#endif // __BADPROG_MEDIATOR_H__
Ship.cpp
#include <iostream>
#include <string>
#include "Ship.h"
#include "IMediator.h"
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
Ship::Ship(std::string name) : _name(name) {
_vectorOre.push_back("Silver");
_vectorOre.push_back("Gold");
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
Ship::~Ship() {
std::cout << __FUNCTION__ << " byebye I was the " << _name << std::endl;
}
//-----------------------------------------------------------------------------
// getName
//-----------------------------------------------------------------------------
const std::string &Ship::getName() const {
return _name;
}
//-----------------------------------------------------------------------------
// sendMessage
//-----------------------------------------------------------------------------
void Ship::sendMessage(IMediator &mediator, const IShip::ENUM_ORE &ore, double latitude, double longitude) {
std::cout << getName() << " is going to send a message to its Mediator:" << std::endl;
std::cout << "\"" << getOreInString(ore) << "\" found! Latitude: " << latitude << " and longitude: " << longitude << "." << std::endl << std::endl;
mediator.spreadMessage(ore, latitude, longitude);
}
//-----------------------------------------------------------------------------
// receiveMessage
//-----------------------------------------------------------------------------
void Ship::receiveMessage(IShip::ENUM_ORE ore, double latitude, double longitude) {
std::cout << this->getName() << " is receiving a message: " << std::endl;
std::cout << "Alright I'm going to extract \"" << getOreInString(ore) << "\" at GPS coordinates (" << latitude << ", " << longitude << "). Let's go!" << std::endl << std::endl;
}
//-----------------------------------------------------------------------------
// receiveMessage
//-----------------------------------------------------------------------------
const std::string Ship::getOreInString(const IShip::ENUM_ORE &ore) const {
return _vectorOre.at(ore);
}
Ship.h
#ifndef __BADPROG_SHIP_H__
#define __BADPROG_SHIP_H__
#include <memory>
#include "IShip.h"
// Badprog.com
class IMediator;
class Ship : public IShip
{
public:
Ship(std::string _name);
virtual ~Ship();
virtual void sendMessage(IMediator &mediator, const IShip::ENUM_ORE &ore, double latitude, double longitude) override;
virtual void receiveMessage(IShip::ENUM_ORE ore, double latitude, double longitude) override;
virtual const std::string &getName() const override;
const std::string getOreInString(const IShip::ENUM_ORE &ore) const;
private:
std::string _name;
std::vector<std::string> _vectorOre;
};
#endif // __BADPROG_SHIP_H__
main.cpp
#include "Ship.h"
#include "Mediator.h"
#include "IMediator.h"
#include "IShip.h"
#include "Mediator.h"
#include "Ship.h"
// Badprog.com
//-----------------------------------------------------------------------------
// main
//-----------------------------------------------------------------------------
int main() {
auto ship1 = std::make_unique<Ship>(std::string("Scout"));
auto ship2 = std::make_unique<Ship>(std::string("Extractor 1"));
auto ship3 = std::make_unique<Ship>(std::string("Extractor 2"));
auto ship4 = std::make_unique<Ship>(std::string("Extractor 3"));
auto ship5 = std::make_unique<Ship>(std::string("Extractor 4"));
auto mediator1 = std::make_unique<Mediator>();
const std::vector<std::unique_ptr<IShip>> &personalVector = mediator1->getVectorOfExtractors();
mediator1->addNewRegistered(std::move(ship2));
mediator1->addNewRegistered(std::move(ship3));
mediator1->addNewRegistered(std::move(ship4));
mediator1->addNewRegistered(std::move(ship5));
std::cout << std::endl;
ship1->sendMessage(*mediator1, IShip::ORE_SILVER, 40.741895, -73.989308);
std::cout << std::endl;
std::cout << ship1->getName() << " is going to send a message:" << std::endl;
ship1->sendMessage(*mediator1, IShip::ORE_GOLD, -33.856769, 151.215158);
std::cout << std::endl;
return 0;
}
Compiling, linking and running
g++ *.cpp -o go.exe -std=c++1y ; ./go.exe
Result
Scout is going to send a message to its Mediator:
"Silver" found! Latitude: 40.7419 and longitude: -73.9893.
Extractor 1 is receiving a message:
Alright I'm going to extract "Silver" at GPS coordinates (40.7419, -73.9893). Let's go!
Extractor 2 is receiving a message:
Alright I'm going to extract "Silver" at GPS coordinates (40.7419, -73.9893). Let's go!
Extractor 3 is receiving a message:
Alright I'm going to extract "Silver" at GPS coordinates (40.7419, -73.9893). Let's go!
Extractor 4 is receiving a message:
Alright I'm going to extract "Silver" at GPS coordinates (40.7419, -73.9893). Let's go!
Scout is going to send a message:
Scout is going to send a message to its Mediator:
"Gold" found! Latitude: -33.8568 and longitude: 151.215.
Extractor 1 is receiving a message:
Alright I'm going to extract "Gold" at GPS coordinates (-33.8568, 151.215). Let's go!
Extractor 2 is receiving a message:
Alright I'm going to extract "Gold" at GPS coordinates (-33.8568, 151.215). Let's go!
Extractor 3 is receiving a message:
Alright I'm going to extract "Gold" at GPS coordinates (-33.8568, 151.215). Let's go!
Extractor 4 is receiving a message:
Alright I'm going to extract "Gold" at GPS coordinates (-33.8568, 151.215). Let's go!
~Mediator byebye I was the Mediator.
~IMediator
~Ship byebye I was the Extractor 1
~IShip
~Ship byebye I was the Extractor 2
~IShip
~Ship byebye I was the Extractor 3
~IShip
~Ship byebye I was the Extractor 4
~IShip
~Ship byebye I was the Scout
~IShip
Conclusion
In our example after sending a message, the Scout doesn't receive any messages from Extractors.
Of course it would have been possible but to preserve a relative simplicity I chose to stay with a basic implementation.
So as you can see, once the Scout have sent its message, Extractors are immediately ready to go to the next ore mine.
And they seem happy to extract silver and gold.
Good job, you got it!
Add new comment