PHP Mediator Design Pattern Поведенческий паттерн

Паттерн Посредник (Mediator) в PHP

Короткий скринкаст: паттерн «Посредник» (Mediator) — объекты общаются не напрямую, а через посредника. Собираем чат-комнату, где одно сообщение уходит всем участникам.

Mediator Pattern — открыть страницу урока

Описание

Посредник убирает прямые связи между объектами и заменяет их общением через один центральный объект. Пользователь User знает только про Mediator, но не про других участников: его метод send() просто вызывает notify() у посредника. Сам посредник ChatRoom хранит список участников и рассылает сообщение всем, кроме отправителя. Добавить нового участника или изменить правило рассылки можно, не трогая класс User — вся логика взаимодействия собрана в одном месте.

UML-диаграмма

classDiagram class Mediator { <<interface>> +notify(sender: User, message: string): array } class ChatRoom { -users: array +addUser(user: User): void +notify(sender: User, message: string): array } class User { +name: string +send(message: string): array +receive(message: string): string } Mediator <|-- ChatRoom : implements ChatRoom o-- User : keeps User ..> Mediator : uses

Код

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, который связывает классы по контрактам, не давая им ссылаться друг на друга напрямую.