Паттерн Посредник (Mediator) в PHP
Короткий скринкаст: паттерн «Посредник» (Mediator) — объекты общаются не напрямую, а через посредника. Собираем чат-комнату, где одно сообщение уходит всем участникам.
Описание
Посредник убирает прямые связи между объектами и заменяет их общением через один центральный объект. Пользователь User знает только про Mediator, но не про других участников: его метод send() просто вызывает notify() у посредника. Сам посредник ChatRoom хранит список участников и рассылает сообщение всем, кроме отправителя. Добавить нового участника или изменить правило рассылки можно, не трогая класс User — вся логика взаимодействия собрана в одном месте.
UML-диаграмма
Код
Mediator.php
interface Mediator
{
public function notify(User $sender, string $message): array;
}
User.php
class User
{
public function __construct(private Mediator $mediator, public readonly string $name) {}
public function send(string $message): array
{
return $this->mediator->notify($this, $message);
}
public function receive(string $message): string
{
return $this->name.' received: '.$message;
}
}
ChatRoom.php
class ChatRoom implements Mediator
{
private array $users = [];
public function addUser(User $user): void
{
$this->users[] = $user;
}
public function notify(User $sender, string $message): array
{
$log = [];
foreach ($this->users as $user) {
if ($user !== $sender) {
$log[] = $user->receive($message);
}
}
return $log;
}
}
demo.php
require_once 'Mediator.php';
require_once 'User.php';
require_once 'ChatRoom.php';
$chat = new ChatRoom;
$alice = new User($chat, 'Alice');
$bob = new User($chat, 'Bob');
$carol = new User($chat, 'Carol');
$chat->addUser($alice);
$chat->addUser($bob);
$chat->addUser($carol);
foreach ($alice->send('Hello, team!') as $line) {
echo $line.PHP_EOL;
}
Пример результата
После php demo.php сообщение Alice доходит до остальных, хотя напрямую она их не знает:
Bob received: Hello, team!
Carol received: Hello, team!
Преимущества
- Участники развязаны: каждый знает только посредника, а не всех остальных.
- Логика взаимодействия собрана в одном классе и не размазана по участникам.
- Новых участников и правила общения можно менять, не трогая существующих.
- Число связей падает с «каждый с каждым» до «каждый с посредником».
Когда использовать
- Когда между объектами много запутанных связей и их сложно отслеживать.
- Когда объектам не нужно знать друг о друге напрямую.
- Когда поведение взаимодействия должно переиспользоваться независимо от объектов.
- Когда логика общения меняется чаще, чем сами участники.
Использование в Laravel
В Laravel роль посредника чаще всего играет шина событий: Illuminate\Events\Dispatcher рассылает событие подписчикам так, что отправитель не знает о получателях — он лишь вызывает event() или dispatch(). Тот же принцип у менеджера уведомлений Illuminate\Notifications\ChannelManager и у контейнера сервисов Illuminate\Container\Container, который связывает классы по контрактам, не давая им ссылаться друг на друга напрямую.