Mediator Pattern : Design Pattern

Jul 20, 2020 By Puneet Verma

The mediator design pattern is a behavioural design pattern that allows communication between objects through a mediator object rather than having objects communicate directly with each other.

This can be useful in situations where you want to reduce the dependencies between objects or where you want to centralize control over communication between objects.

Let's break it down 

Imagine you have two objects in your code, ObjectA and ObjectB, that need to communicate with each other. Without using the mediator design pattern, ObjectA might directly call a method on ObjectB to send it a message like this:

$objectB->receiveMessage('Hello, world!');

However, this approach has a couple of problems:

  1. It creates a direct dependency between ObjectA and ObjectB, meaning that if you want to change the way ObjectA and ObjectB interact, you have to modify both objects.

  2. It can be difficult to add additional objects to the system because they would have to be directly connected to both ObjectA and ObjectB in order to communicate with them.

The mediator design pattern addresses these problems by introducing a third object, the mediator, which acts as a go-between for ObjectA and ObjectB. Instead of calling a method on ObjectB directly, ObjectA sends a message to the mediator, which then forwards the message to ObjectB. This has a couple of benefits:

  1. It decouples ObjectA and ObjectB, so they don't have a direct dependency on each other. It makes changing the way they interact easier because you only have to modify the mediator.

  2. It makes adding additional objects to the system easier because they only have to communicate with the mediator to send and receive messages.

Example 1 

Here is another example to illustrate how the mediator design pattern works:

Imagine you have a chat application that allows users to send messages to each other. Without using the mediator design pattern, you might create a User the class that has a method for sending messages directly to another user, like this:

 

class User {
  private $name;

  public function __construct($name) {
    $this->name = $name;
  }

  public function sendMessage($message, User $recipient) {
    // Send the message to the recipient
  }
}

$user1 = new User('Alice');
$user2 = new User('Bob');

$user1->sendMessage('Hello, Bob!', $user2);

This approach has a couple of problems:

  1. It creates a direct dependency between the User objects, meaning that if you want to change the way users send and receive messages, you have to modify both the User class and any code that uses it.

  2. It can be difficult to add additional functionality to the chat application, such as the ability to send messages to a group of users or to persist messages in a database.

The mediator design pattern addresses these problems by introducing a third object, the ChatMediator, which acts as a go-between for the User objects. Instead of calling a method on another user directly, a user sends a message to the mediator, which then forwards the message to the appropriate recipients. This has a couple of benefits:

  1. It decouples the User objects, so they don't have a direct dependency on each other. This makes it easier to change the way users send and receive messages, because you only have to modify the mediator.

  2. It makes it easier to add additional functionality to the chat application, because you can modify the mediator to handle tasks such as sending messages to a group of users or persisting messages in a database.

Here is an example of how you might implement the mediator design pattern in this chat application:

 

class ChatMediator {
  private $users;

  public function __construct() {
    $this->users = [];
  }

  public function addUser(User $user) {
    $this->users[] = $user;
  }

  public function sendMessage($message, User $sender) {
    foreach ($this->users as $user) {
      if ($user !== $sender) {
        $user->receiveMessage($message);
      }
    }
  }
}

class User {
  private $name;
  private $mediator;

  public function __construct($name, ChatMediator $mediator) {
    $this->name = $name;
    $this->mediator = $mediator;
    $mediator->addUser($this);
  }

  public function getName() {
    return $this->name;
  }

  public function sendMessage($message) {
    $this->mediator->sendMessage($message, $this);
  }

  public function receiveMessage($message) {
    echo "$this->name received message: $message\n";
  }

 

Example 2

Imagine you have a system that allows customers to place orders with a company, and the orders are fulfilled by a warehouse. Without using the mediator design pattern, you might create a Customer the class has a method for placing an order directly with the warehouse, like this:

class Customer {
  private $name;

  public function __construct($name) {
    $this->name = $name;
  }

  public function placeOrder($product, Warehouse $warehouse) {
    $warehouse->fulfillOrder($product);
  }
}

$customer = new Customer('Alice');
$warehouse = new Warehouse;

$customer->placeOrder('Widget', $warehouse);

This approach has a couple of problems:

  1. It creates a direct dependency between the Customer and Warehouse classes, meaning that if you want to change the way orders are placed or fulfilled, you have to modify both classes.

  2. It can be difficult to add additional functionality to the system, such as tracking the status of orders or sending notifications to customers when their orders are shipped.

The mediator design pattern addresses these problems by introducing a third object, the OrderMediator, which acts as a go-between for the Customer and Warehouse objects. Instead of calling a method on the warehouse directly, the customer sends an order to the mediator, which then forwards the order to the warehouse for fulfilment. This has a couple of benefits:

  1. It decouples the Customer and Warehouse Objects, so they don't have a direct dependency on each other. This makes it easier to change the way orders are placed or fulfilled because you only have to modify the mediator.

  2. It makes adding additional functionality to the system easier because you can modify the mediator to handle tasks such as tracking the status of orders or sending notifications to customers.

Here is an example of how you might implement the mediator design pattern in this system:

class OrderMediator {
  private $warehouse;

  public function __construct(Warehouse $warehouse) {
    $this->warehouse = $warehouse;
  }

  public function placeOrder($product, Customer $customer) {
    $this->warehouse->fulfillOrder($product);
    // Send notification to customer
  }
}

class Customer {
  private $name;
  private $mediator;

  public function __construct($name, OrderMediator $mediator) {
    $this->name = $name;
    $this->mediator = $mediator;
  }

  public function placeOrder($product) {
    $this->mediator->placeOrder($product, $this);
  }
}

class Warehouse {
  public function fulfillOrder($product) {
    // Fulfill the order
  }
}

$customer = new Customer('Alice', new OrderMediator(new Warehouse));
$customer->placeOrder('Widget');

 

In this example, the OrderMediator class handles the placement of orders by forwarding them to the warehouse for fulfillment and sending a notification to the customer. The Customer class communicates with the mediator instead of the warehouse directly, which reduces the dependencies between these objects and makes it easier to modify . 

 

example 3

Imagine you have a program that displays a list of items from a database, and allows the user to filter the list by category. Without using the mediator design pattern, you might create a Filter class that has a method for applying the filter directly to the list of items, like this:

class Filter {
  public function apply($items, $category) {
    // Filter the items by category and return the result
  }
}

$filter = new Filter;
$items = $filter->apply($items, 'clothing');

 

This approach has a couple of problems:

  1. It creates a direct dependency between the Filter class and the list of items, meaning that if you want to change the way the filter works, you have to modify both the Filter class and any code that uses it.

  2. It can be difficult to add additional filters or change the way filters are applied because you would have to modify the Filter class or the code that calls it.

The mediator design pattern addresses these problems by introducing a third object the FilterMediator, which acts as a go-between for the Filter class and the list of items. Instead of calling a method on the Filter class directly, the mediator applies the filter to the list of items and returns the result. This has a couple of benefits:

  1. It decouples the Filter Class and the list of items, so they don't have a direct dependency on each other. This makes it easier to change the way the filter works because you only have to modify the mediator.

  2. It makes it easier to add additional filters or change the way filters are applied because you can modify the mediator to handle these tasks.

Here is an example of how you might implement the mediator design pattern in this application:

 

class FilterMediator {
  private $filters;

  public function __construct() {
    $this->filters = [];
  }

  public function addFilter(Filter $filter) {
    $this->filters[] = $filter;
  }

  public function applyFilters($items, $categories) {
    foreach ($this->filters as $filter) {
      $items = $filter->apply($items, $categories);
    }
    return $items;
  }
}

class Filter {
  public function apply($items, $categories) {
    // Filter the items by category and return the result
  }
}

$mediator = new FilterMediator;
$filter = new Filter;

$mediator->addFilter($filter);
$items = $mediator->applyFilters($items, ['clothing']);

 

example 4

Here is a practical example of how you might use the mediator design pattern in PHP to manage interactions between objects in your code.

Imagine you have a web application that displays a list of articles and allows the user to filter the list by keyword. You could use the mediator design pattern to decouple the Article class and the Filter class, like this:

<?php

// Mediator interface
interface ArticleMediator {
  public function filterArticles($keywords, $articles);
}

// Concrete mediator
class ConcreteArticleMediator implements ArticleMediator {
  private $filters;

  public function __construct() {
    $this->filters = [];
  }

  public function addFilter(Filter $filter) {
    $this->filters[] = $filter;
  }

  public function filterArticles($keywords, $articles) {
    foreach ($this->filters as $filter) {
      $articles = $filter->apply($articles, $keywords);
    }
    return $articles;
  }
}

// Colleague interface
interface Filter {
  public function apply($articles, $keywords);
}

// Concrete colleague
class KeywordFilter implements Filter {
  public function apply($articles, $keywords) {
    $filteredArticles = [];
    foreach ($articles as $article) {
      if (stripos($article->getTitle(), $keywords) !== false) {
        $filteredArticles[] = $article;
      }
    }
    return $filteredArticles;
  }
}

// Concrete colleague
class AuthorFilter implements Filter {
  public function apply($articles, $author) {
    $filteredArticles = [];
    foreach ($articles as $article) {
      if ($article->getAuthor() == $author) {
        $filteredArticles[] = $article;
      }
    }
    return $filteredArticles;
  }
}

// Colleague
class Article {
  private $title;
  private $author;

  public function __construct($title, $author) {
    $this->title = $title;
    $this->author = $author;
  }

  public function getTitle() {
    return $this->title;
  }

  public function getAuthor() {
    return $this->author;
  }
}

// Client code
$articles = [
  new Article('PHP Design Patterns', 'John Doe'),
  new Article('Clean Code', 'Jane Smith'),
  new Article('Refactoring', 'John Doe'),
];

$mediator = new ConcreteArticleMediator;
$keywordFilter = new KeywordFilter;
$authorFilter = new AuthorFilter;

$mediator->addFilter($keywordFilter);
$mediator->addFilter($authorFilter);

$filteredArticles = $mediator->filterArticles('John Doe', $articles);

foreach ($filteredArticles as $article) {
  echo $article->getTitle() . "\n";
}

 

In this example, the ArticleMediator interface defines a method for filtering a list of articles, and the ConcreteArticleMediator class implements this method by applying a set of filters to the list.

 

example 5

<?php

// Mediator interface
interface Mediator {
  public function send($message, $colleague);
}

// Colleague interface
interface Colleague {
  public function setMediator(Mediator $mediator);
  public function setColleagueEnabled($enabled);
}

// Concrete mediator
class ConcreteMediator implements Mediator {
  private $colleague1;
  private $colleague2;

  public function setColleague1(Colleague $colleague) {
    $this->colleague1 = $colleague;
  }

  public function setColleague2(Colleague $colleague) {
    $this->colleague2 = $colleague;
  }

  public function send($message, Colleague $colleague) {
    if ($colleague === $this->colleague1) {
      $this->colleague2->receive($message);
    } else {
      $this->colleague1->receive($message);
    }
  }
}

// Concrete colleague 1
class ConcreteColleague1 implements Colleague {
  private $mediator;
  private $enabled = true;

  public function setMediator(Mediator $mediator) {
    $this->mediator = $mediator;
  }

  public function setColleagueEnabled($enabled) {
    $this->enabled = $enabled;
  }

  public function send($message) {
    if ($this->enabled) {
      $this->mediator->send($message, $this);
    }
  }

  public function receive($message) {
    echo "Colleague 1 received message: $message\n";
  }
}

// Concrete colleague 2
class ConcreteColleague2 implements Colleague {
  private $mediator;
  private $enabled = true;

  public function setMediator(Mediator $mediator) {
    $this->mediator = $mediator;
  }

  public function setColleagueEnabled($enabled) {
    $this->enabled = $enabled;
  }

  public function send($message) {
    if ($this->enabled) {
      $this->mediator->send($message, $this);
    }
  }

  public function receive($message) {
    echo "Colleague 2 received message: $message\n";
  }
}

// Client code
$mediator = new ConcreteMediator();

$colleague1 = new ConcreteColleague1();
$colleague1->setMediator($mediator);
$mediator->setColleague1($colleague1);

$colleague2 = new ConcreteColleague2();
$colleague2->setMediator($mediator);
$mediator->setColleague2($colleague2);

$colleague1->send("Hello from colleague 1");
$colleague2->send("Hello from colleague 2");

 

In this example, the Mediator class acts as the mediator between the Colleague objects. The Colleague objects communicate with each other through the mediator, using the send() and receive() methods. This allows the Colleague objects to be decoupled from each other, and allows the communication between them to be centralized and controlled through the mediator.

The mediator design pattern is often used in conjunction with the observer pattern, to allow objects to subscribe to events and communicate with each other when those events occur.

 

 

In PHP, you can implement the mediator design pattern by defining a mediator interface or abstract class that specifies the methods that must be implemented by concrete mediator classes. You can then create concrete mediator classes that implement the mediator interface and use these mediators to manage the interactions between a set of objects.

Here is an example of how you might implement the mediator design pattern in PHP:

 

<?php

// Mediator interface
interface Mediator {
  public function send($message, Colleague $colleague);
}

// Concrete mediator
class ConcreteMediator implements Mediator {
  private $colleague1;
  private $colleague2;

  public function setColleague1(Colleague $colleague) {
    $this->colleague1 = $colleague;
  }

  public function setColleague2(Colleague $colleague) {
    $this->colleague2 = $colleague;
  }

  public function send($message, Colleague $colleague) {
    if ($colleague === $this->colleague1) {
      $this->colleague2->notify($message);
    } else {
      $this->colleague1->notify($message);
    }
  }
}

// Colleague interface
interface Colleague {
  public function setMediator(Mediator $mediator);
  public function notify($message);
}

// Concrete colleague
class ConcreteColleague1 implements Colleague {
  private $mediator;

  public function setMediator(Mediator $mediator) {
    $this->mediator = $mediator;
  }

  public function notify($message) {
    echo "Colleague1 gets message: $message\n";
  }

  public function send($message) {
    $this->mediator->send($message, $this);
  }
}

// Concrete colleague
class ConcreteColleague2 implements Colleague {
  private $mediator;

  public function setMediator(Mediator $mediator) {
    $this->mediator = $mediator;
  }

  public function notify($message) {
    echo "Colleague2 gets message: $message\n";
  }

  public function send($message) {
    $this->mediator->send($message, $this);
  }
}

// Client code
$c1 = new ConcreteColleague1;
$c2 = new ConcreteColleague2;
$mediator = new ConcreteMediator;

$mediator->setColleague1($c1);
$mediator->setColleague2($c2);

$c1->setMediator($mediator);
$c2->setMediator($mediator);

$c1->send('How are you?');
$c2->send('Fine, thanks');

In this example, the Mediator interface defines a method for sending messages between colleagues, and the ConcreteMediator class implements this method by forwarding the message to the appropriate colleague. The Colleague interface defines a method for receiving notifications from the mediator

 

 

 

 

This pattern provides a mediator class which normally handles all the communications between different classes. Example:- We are demonstrating mediator pattern by the example of a chat room where multiple users can send the message to the chat room, and it is the responsibility of chat room to show the messages to all users.