C++ Mediator Pattern Example

In this article, we will learn how to use and implement the Mediator Pattern in C++ with an example.

A mediator pattern is a behavioral design pattern that lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.

C++ Mediator Pattern Example

The below diagram shows the generic structure of the Mediator Pattern:

Let's refer to the above structure to create an example to demonstrates the usage of the Mediator Pattern.
#include <iostream>
#include <vector>
#include <string>

class Mediator;

/*
 * Colleague classes
 * each colleague communicates with its mediator whenever
 * it would have otherwise communicated with another colleague
 */
class Colleague
{
public:
    Colleague( Mediator* const m, const unsigned int i ) :
        mediator( m ), id( i ) {}

    virtual ~Colleague() {}

    unsigned int getID()
    {
        return id;
    }

    virtual void send( std::string ) = 0;
    virtual void receive( std::string ) = 0;

protected:
    Mediator *mediator;
    unsigned int id;
};

class ConcreteColleague : public Colleague
{
public:
    ConcreteColleague( Mediator* const m, const unsigned int i ) :
        Colleague( m, i ) {}

    ~ConcreteColleague() {}

    void send( std::string msg );

    void receive( std::string msg )
    {
        std::cout << "Message '" << msg << "' received by Colleague " << id << std::endl;
    }
};

/*
 * Mediator
 * defines an interface for communicating with Colleague objects
 */
class Mediator
{
public:
    virtual ~Mediator() {}

    virtual void add( Colleague* const c ) = 0;
    virtual void distribute( Colleague* const sender, std::string msg ) = 0;

protected:
    Mediator() {}
};

/*
 * Concrete Mediator
 * implements cooperative behavior by coordinating Colleague objects
 * and knows its colleagues
 */
class ConcreteMediator : public Mediator
{
public:
    ~ConcreteMediator()
    {
        for ( unsigned int i = 0; i < colleagues.size(); i++ )
        {
            delete colleagues[ i ];
        }
        colleagues.clear();
    }

    void add( Colleague* const c )
    {
        colleagues.push_back( c );
    }

    void distribute( Colleague* const sender, std::string msg )
    {
        for ( unsigned int i = 0; i < colleagues.size(); i++ )
        {
            if ( colleagues.at( i )->getID() != sender->getID() )
            {
                colleagues.at( i )->receive( msg );
            }
        }
    }

private:
    std::vector<Colleague*> colleagues;
};

void ConcreteColleague::send( std::string msg )
{
    std::cout << "Message '"<< msg << "' sent by Colleague " << id << std::endl;
    mediator->distribute( this, msg );
}


int main()
{
    Mediator *mediator = new ConcreteMediator();

    Colleague *c1 = new ConcreteColleague( mediator, 1 );
    Colleague *c2 = new ConcreteColleague( mediator, 2 );
    Colleague *c3 = new ConcreteColleague( mediator, 3 );

    mediator->add( c1 );
    mediator->add( c2 );
    mediator->add( c3 );

    c1->send( "Hi!" );
    c3->send( "Hello!" );

    delete mediator;
    return 0;
}

Output

Message 'Hi!' sent by Colleague 1
Message 'Hi!' received by Colleague 2
Message 'Hi!' received by Colleague 3
Message 'Hello!' sent by Colleague 3
Message 'Hello!' received by Colleague 1
Message 'Hello!' received by Colleague 2

When to Use Mediator Pattern

  • Use the Mediator pattern when it’s hard to change some of the classes because they are tightly coupled to a bunch of other classes.
  • Use the pattern when you can’t reuse a component in a different program because it’s too dependent on other components.
  • Use the Mediator when you find yourself creating tons of component subclasses just to reuse some basic behavior in various contexts

Related C++ Design Patterns