C++ - Design pattern - Mediator

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:

  1. Registration;
  2. 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! cool

 

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.