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:
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.
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:
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.
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:
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.
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:
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.
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:
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.
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:
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.
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:
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.
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:
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.
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.