Рассматривается поведенческий шаблон проектирования Mediator, условия его применения, пример реализации.
Содержание
- Применение
- Реализация
- Итоги
Применение
Представьте, что вы разрабатываете многофункциональное приложение, усердно поработали и создали множество классов, взаимодействующих друг с другом. Но на каком-то этапе разработки может возникнуть примерно такая картина.
Что мы имеем? Компоненты тесно связаны, поскольку взаимодействуют напрямую друг с другом. Такие компоненты сложно изменять, тестировать, повторно использовать, т.к. они связаны с другими.
Если назвать полученное одним словом, то просится слово хаос. Такое приложение сложно поддерживать, развивать, в нем наверняка куча повторного кода и прочие прелести.
Избежать всего этого помогает шаблон Mediator, он же Посредник. Основная цель его использования — уменьшить связи между между компонентами приложения за счет перемещения взаимодействий в один отдельный компонент-посредник. В этом случае остальным компонентам приложения не надо знать друг о друге, достаточно будет взаимодействовать только с одним посредником, который все разрулит. Использование посредника уменьшается связанность элементов, что позволяет их многократно использовать. Также облегчается введение в приложение новых элементов, ведь теперь надо будет доработать в основном один класс посредника. Хорошим примером посредника являются диспетчерские службы, которые все взаимосвязи подразделений или исполнителей замыкают на себя, предоставляя последним заниматься своими непосредственными обязанностями.
С применением шаблона Mediator структура приведенного выше приложения станет такой.
Реализация
Для реализации шаблона нам понадобятся:
- интерфейс IMediator — декларирует методы для взаимодействия компонентов приложения с посредником;
- класс Mediator — обеспечивает взаимодействие конкретных компонентов на основе интерфейса IMediator;
- абстрактный класс BaseComponent — определяет набор обязательных свойств и методов дочерних компонентов для связи с другими компонентами через Посредника;
- класс Component — определяет бизнес-логику компонентов приложения, наследуется от абстрактного класса BaseComponent.
Итак, в нашем приложении будут использоваться различные компоненты, взаимодействующие друг с другом через строковые сообщения через объект посредника. Начнем с интерфейса посредника, в нашем случае достаточно декларировать методы, обеспечивающие управление сообщениями.
interface IMediator
{
public function processMessage(Component $component, string $message);
public function getComponents($arr);
}
Класс конкретного посредника Mediator, кроме обработки сообщения, фиксирует время прихода сообщения и на основе входных параметров определяет получателя.
class Mediator implements IMediator
{
private $components;
public function getComponents($arr)
{
$this->components = $arr;
}
public function processMessage(Component $component, string $message)
{
$senderName = $component->getName(); //имя отправителя
$time = date('d.m.Y H:i');
$recipient = $this->getRecipient($senderName, $message); //определяем получателя
if (isset($recipient)) {
$recipient->getMessage($message, $time);
} else {
echo 'Посредник: компонент не найден';
}
}
//определяет получателя сообщения
private function getRecipient(string $name, string $message = '')
{
$recipient;
if (isset($this->components)) {
switch ($this->components[$name]->getName()) {
case 'component1':
$recipient = $this->components['component2'];
break;
case 'component2':
$recipient = $this->components['component1'];;
break;
default:
$recipient = null;
}
}
return $recipient;
}
}
Родительский класс компонентов, определяет обязательные свойства и методы компонентов приложения для их взаимодействия.
abstract class BaseComponent
{
private $name;
private $mediator; //ссылка на посредник
abstract public function sendMessage(string $message);
abstract public function getMessage(string $message);
public function getName() {
return $this->name;
}
}
Определим конкретный компонент приложения.
class Component extends BaseComponent
{
private $name;
private $mediator;
private $components;
public function __construct(string $name, Mediator $mediator)
{
$this->name = $name;
$this->mediator = $mediator;
}
public function sendMessage($message)
{
$this->mediator->processMessage($this, $message);
}
public function getMessage($message)
{
//код обработки поступившего сообщения ...
$time = date('d.m.Y H:i');
echo '<b>' . $this->getName() . '</b> обработал сообщение '
. '"' . $message . '"' . ' в ' . $time;
}
public function getName()
{
return $this->name;
}
}
Вот как это будет работать в приложении.
$mediator = new Mediator();
$component1 = new Component('component1', $mediator);
$component2 = new Component('component2', $mediator);
$components = [];
$components[$component1->getName()] = $component1;
$components[$component2->getName()] = $component2;
$mediator->getComponents($components);
$message = 'Сообщение1';
echo '<b>' . $component1->getName() . '</b> отправил сообщение "' . $message . '"<br>';
$component1->sendMessage($message);
В приложении создается посредник $mediator и компоненты программы $component1 и $component2. Обратите внимание, компоненты ничего не знают друг о друге. При возникновении какого-либо события в компоненте(мы имитировали сообщение $message), ему достаточно лишь отправить об этом сообщение, a его обработку в системе берет на себя посредник $mediator. Результат работы примера будет таким.
component1 отправил сообщение "Сообщение1"
component2 обработал сообщение "Сообщение1" в 14.04.2024 09:22
Итоги
Итак, было показано, что использование шаблона проектирования Mediator позволяет:
- существенно упростить архитектуру программного обеспечения за счет ослабления связей между элементами программы;
- сосредоточить управление взаимодействием элементов в одном месте;
- упростить взаимодействие объектов;
- добиться повторного использования кода без его модификации;
- упростить доработку ПО при расширении функционала программы.
Перейти к списку шаблонов проектирования.