Что это такое
Паттерн Наблюдатель позволяет одному объекту объявлять о событиях многим подписчикам, не зная, кто они. Подписчики регистрируются один раз; после этого каждое событие автоматически доходит до всех.
Издатель зависит только от небольшого интерфейса подписчика — новые виды слушателей появляются без изменения ни одной строки в издателе. В PHP для этого паттерна даже есть готовые интерфейсы SplSubject и SplObserver; собственный небольшой интерфейс, как ниже, читается не хуже.
Пример: одно оповещение — каждому подписчику
Монитор ведёт список подписчиков. Когда что-то происходит, он оповещает весь список — сегодня это письмо-оповещение и панель; завтра — сообщение в чат, достаточно его подписать.
Публикуем один раз, доставляем всем
Один вызов alert(), две доставки. Монитор не имеет представления, что такое письмо или панель, — он знает лишь о том, что их можно оповестить.
Вывод: PHP-скрипт
<?php
interface Subscriber {
public function notify(string $event): void;
}
class EmailAlert implements Subscriber {
public function __construct(private string $addr) {}
public function notify(string $event): void {
echo "письмо на ", $this->addr, ": ", $event, "\n";
}
}
class Dashboard implements Subscriber {
public function notify(string $event): void {
echo "панель показывает: ", $event, "\n";
}
}
// Издатель хранит список и оповещает всех, кто в нём есть.
class Monitor {
/** @var Subscriber[] */
private array $subscribers = [];
public function subscribe(Subscriber $s): void {
$this->subscribers[] = $s;
}
public function alert(string $event): void {
foreach ($this->subscribers as $s) {
$s->notify($event);
}
}
}
$monitor = new Monitor();
$monitor->subscribe(new EmailAlert("ops@example.com"));
$monitor->subscribe(new Dashboard());
$monitor->alert("диск почти заполнен");
Когда его применять
Применяйте, когда на одни и те же события должны реагировать несколько частей системы и список реагирующих меняется — обновления интерфейса, оповещения, сброс кеша, ленты активности.
Не применяйте, когда получатель ровно один и он фиксирован, — вызывайте его напрямую. Диспетчер событий в более крупном PHP-приложении — это тот же паттерн, только выросший: именованные события, много слушателей, одна точка рассылки.
Плюсы и минусы
Плюсы: издатель и подписчики остаются слабо связанными; подписчики приходят и уходят во время выполнения; новые реакции не требуют изменений в издателе.
Минусы: поток управления становится невидимым — на вопрос «кто на это реагирует?» приходится искать ответ; медленный или падающий подписчик может застопорить цикл оповещения; забытые отписки приводят к утечкам.