O Padrão de Projeto Mediator é muito utilizado quando temos muitos relacionamentos entre objetos. Com isso, ele assume o comando e faz a mediação entre os objetos. Ou seja, ele restringe as comunicações diretas entre os objetos e mediador entra em ação para assumir essa responsabilidade.
“Definir um objeto que encapsula a forma como um conjunto de objetos interage. O Mediator promove o acoplamento fraco ao evitar que os objetos se refiram uns aos outros explicitamente e permite variar suas interações independentemente.”
(Gamma)
Olhando esse diagrama de classe parece um pouco confuso o uso do Padrão Mediator. Porém, conseguimos observar que temos claramente os Mediadores e os Colaboradores. É um jogo de comunicação simples. A interface do Mediador é usada pelos colaboradores para iniciar a comunicação e receber notificações e o Mediador recebe e repassa as requisições para os destinatários.
Um excelente exemplo para exemplificar o uso deste padrão. Pense em um aeroporto, por exemplo, o de Guarulhos (GRU) em São Paulo. Temos vários aviões que trafegam e precisam em algum momento se comunicarem para estarem em sincronismo para não gerar acidentes ou imprevistos. Só que imagina 10 aviões ao mesmo tempo tendo comunicação direta entre os pilotos. Tudo isso geraria um caos. Por este motivo, eles têm o controlador de tráfego aéreo que faz o papel de Mediator e controla a comunicação entre as aeronaves.
Fiz uma representação em um diagrama de classe onde você poderá comparar as classes e como funcionam os seus relacionamentos:
Vamos ao código então! Começando pelas classes abstratas e interfaces que gerarão os contratos de comunicação entre os participantes:
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\Mediator; interface AirTrafficControl { public function addAirplane(Airplane $airplane): void; public function notifyAirplane(Airplane $airplane, string $message): void; }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\Mediator; abstract class Airplane { protected AirTrafficControl $airTrafficControl; protected string $message; public function __construct(AirTrafficControl $airTrafficControl) { $this->airTrafficControl = $airTrafficControl; } abstract public function sendMessage(string $message): void; abstract public function receiveMessage(string $message): void; public function getMessage(): string { return $this->message; } }
Agora representamos o GRU Air Traffic Control como Concrete Mediator e as classes Airbus, Boing e Cessna como Concrete Colleagues.
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\Mediator; use SplObjectStorage; final class GRUAirTrafficControl implements AirTrafficControl { private SplObjectStorage $airplane; public function __construct() { $this->airplane = new SplObjectStorage(); } public function addAirplane(Airplane $airplane): void { $this->airplane->attach($airplane); } public function notifyAirplane(Airplane $airplaneReceiver, string $message): void { while ($this->airplane->valid()) { $airplane = $this->airplane->current(); if ($airplane !== $airplaneReceiver) { $airplane->receiveMessage($message); } $this->airplane->next(); } } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\Mediator; class Airbus extends Airplane { public function sendMessage(string $message): void { $this->airTrafficControl->notifyAirplane($this, $message); } public function receiveMessage(string $message): void { $this->message = sprintf("Airbus received message: %s", $message); } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\Mediator; class Boing extends Airplane { public function sendMessage(string $message): void { $this->airTrafficControl->notifyAirplane($this, $message); } public function receiveMessage(string $message): void { $this->message = sprintf("Boing received message: %s", $message); } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\Mediator; class Cessna extends Airplane { public function sendMessage(string $message): void { $this->airTrafficControl->notifyAirplane($this, $message); } public function receiveMessage(string $message): void { $this->message = sprintf("Cessna received message: %s", $message); } }
A relação de comunicação efetiva com o Mediador você consegue observar através da implementação dos testes.
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Tests\Behavioral\Mediator; use Growthdev\DesignPatterns\Behavioral\Mediator\Airbus; use Growthdev\DesignPatterns\Behavioral\Mediator\Boing; use Growthdev\DesignPatterns\Behavioral\Mediator\Cessna; use Growthdev\DesignPatterns\Behavioral\Mediator\GRUAirTrafficControl; use PHPUnit\Framework\TestCase; final class AirTrafficControlTest extends TestCase { public function testAirTrafficControlOfTheBoingSendingMessage(): void { $airTrafficControl = new GRUAirTrafficControl(); $boing = new Boing($airTrafficControl); $airbus = new Airbus($airTrafficControl); $cessna = new Cessna($airTrafficControl); $airTrafficControl->addAirplane($boing); $airTrafficControl->addAirplane($airbus); $airTrafficControl->addAirplane($cessna); $boing->sendMessage("BOING: I am flying!"); $this->assertEquals('Airbus received message: BOING: I am flying!', $airbus->getMessage()); $this->assertEquals('Cessna received message: BOING: I am flying!', $cessna->getMessage()); } public function testAirTrafficControlOfTheAirbusSendingMessage(): void { $airTrafficControl = new GRUAirTrafficControl(); $boing = new Boing($airTrafficControl); $airbus = new Airbus($airTrafficControl); $cessna = new Cessna($airTrafficControl); $airTrafficControl->addAirplane($boing); $airTrafficControl->addAirplane($airbus); $airTrafficControl->addAirplane($cessna); $airbus->sendMessage("AIRBUS: I am flying!"); $this->assertEquals('Boing received message: AIRBUS: I am flying!', $boing->getMessage()); $this->assertEquals('Cessna received message: AIRBUS: I am flying!', $cessna->getMessage()); } public function testAirTrafficControlOfTheCessnaSendingMessage(): void { $airTrafficControl = new GRUAirTrafficControl(); $boing = new Boing($airTrafficControl); $airbus = new Airbus($airTrafficControl); $cessna = new Cessna($airTrafficControl); $airTrafficControl->addAirplane($boing); $airTrafficControl->addAirplane($airbus); $airTrafficControl->addAirplane($cessna); $cessna->sendMessage("CESSNA: I am flying!"); $this->assertEquals('Boing received message: CESSNA: I am flying!', $boing->getMessage()); $this->assertEquals('Airbus received message: CESSNA: I am flying!', $airbus->getMessage()); } }
Se você estiver acompanhando esta série de artigos sobre Padrões de Projetos. Você vai se deparar com padrões muitos similares. E dependendo da forma como você utilizar, você pode estar transformando um padrão em outro sem nem perceber. Por exemplo, se você tiver apenas um mediador ativo e as classes Collegues forem passivas, você acaba transformando sua implementação no Padrão Facade. Fique atento a estes detalhes.
Vantagens e desvantagens do Padrão Mediator
Como você pode perceber, o Single Responsibility Principle (SRP) é um ponto forte deste pattern além do é Open Closed Principle (OCP) que nos dá facilidade em introduzir novos mediadores assim como novos Collegues sem comprometer o que já existe.
No meu ponto de vista a principal desvantagem deste padrão é que você delega muita responsabilidade para um indivíduo mediador. Isso pode aumentar sua complexidade e o transformar em um God Object. De toda forma, sabemos que nada é perfeito. Então, usando com sabedoria temos bons frutos com o uso deste pattern.
Todos os exemplos de todos os artigos sobre Padrões de Projetos, você encontra no meu Gitgub:
https://github.com/growthdev-repo/design-patterns
Espero que você tenha tirado proveito deste artigo e não deixe de comentar e compartilhar com outros profissionais para que eles possam se beneficiar deste conteúdo assim como você se beneficiou. Até o próximo artigo!
Confiança Sempre!!!
Fontes:
- Gamma, et al. Padrões de Projeto: soluções reutilizáveis de software orientado a objetos, Bookman, 2007.
- https://javapapers.com/design-patterns/mediator-design-pattern/
- https://codepumpkin.com/mediator-design-pattern/
- https://www.journaldev.com/1730/mediator-design-pattern-java
- http://www.design-patterns-stories.com/patterns/Mediator/
Seja o primeiro a comentar