Purpose
The purpose of the design pattern
This pattern provides an easy way to decouple many components working together. It is a good alternative to Observer IF you have a "central intelligence", like a controller (but not in the sense of the MVC).
All components (called Colleague) are only coupled to the Mediator interface and it is a good thing because in OOP, one good friend is better than many. This is the key-feature of this pattern.
UML
UML design pattern diagram
Code
Code snippets
Mediator
Mediator Interface which defines a getUser method signature.
namespace DesignPatterns\Behavioral\Mediator;
interface Mediator
{
public function getUser(string $username): string;
}
Colleague
Abstract class Colleague which eliminates boilerplate code throughout any concrete subclasses which extend it. The setMediator method is final meaning it cannot be overridden by a sub class.
namespace DesignPatterns\Behavioral\Mediator;
abstract class Colleague
{
protected Mediator $mediator;
final public function setMediator(Mediator $mediator)
{
$this->mediator = $mediator;
}
}
Ui
Ui extends Colleague and contains an outputUserInfo method. This method references the mediators getUser method.
namespace DesignPatterns\Behavioral\Mediator;
class Ui extends Colleague
{
public function outputUserInfo(string $username)
{
echo $this->mediator->getUser($username);
}
}
UserRepository
UserRepository extends Colleague and has a getUserName method.
namespace DesignPatterns\Behavioral\Mediator;
class UserRepository extends Colleague
{
public function getUserName(string $user): string
{
return 'User: ' . $user;
}
}
UserRepositoryUiMediator
The UserRepositoryUiMediator expects the UserRepository and Ui classes as construct parameters. This class proxies methods within those classes.
namespace DesignPatterns\Behavioral\Mediator;
class UserRepositoryUiMediator implements Mediator
{
public function __construct(private UserRepository $userRepository, private Ui $ui)
{
$this->userRepository->setMediator($this);
$this->ui->setMediator($this);
}
public function printInfoAbout(string $user)
{
$this->ui->outputUserInfo($user);
}
public function getUser(string $username): string
{
return $this->userRepository->getUserName($username);
}
}
MediatorTest
MediatorTest example below. The mediator design pattern decouples components which all work together. A separation of concerns allows code to be reused and switched out easily.
namespace DesignPatterns\Tests\Mediator\Tests;
use DesignPatterns\Behavioral\Mediator\Ui;
use DesignPatterns\Behavioral\Mediator\UserRepository;
use DesignPatterns\Behavioral\Mediator\UserRepositoryUiMediator;
use PHPUnit\Framework\TestCase;
class MediatorTest extends TestCase
{
public function testOutputHelloWorld()
{
$mediator = new UserRepositoryUiMediator(new UserRepository(), new Ui());
$this->expectOutputString('User: Dominik');
$mediator->printInfoAbout('Dominik');
}
}