Что такое паттерн Observer (Наблюдатель)?

Ответ

Observer — это поведенческий паттерн проектирования, который создает механизм подписки для уведомления нескольких объектов (наблюдателей) об изменениях состояния другого объекта (субъекта). Он реализует отношение «один ко многим».

Зачем он нужен? Чтобы обеспечить слабую связанность: субъекту не нужно знать конкретные классы наблюдателей, а только общий интерфейс. Это упрощает добавление новых наблюдателей и переиспользование кода.

Пример реализации на PHP:

// Интерфейс Наблюдателя
interface ObserverInterface {
    public function update(SubjectInterface $subject): void;
}

// Интерфейс Субъекта
interface SubjectInterface {
    public function attach(ObserverInterface $observer): void;
    public function detach(ObserverInterface $observer): void;
    public function notify(): void;
}

// Конкретный Субъект (например, система заказов)
class OrderService implements SubjectInterface {
    private $observers = [];
    private $orderStatus;

    public function attach(ObserverInterface $observer): void {
        $this->observers[] = $observer;
    }

    public function notify(): void {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function setStatus(string $status): void {
        $this->orderStatus = $status;
        $this->notify(); // Уведомляем всех подписчиков об изменении
    }

    public function getStatus(): string {
        return $this->orderStatus;
    }
}

// Конкретный Наблюдатель (например, сервис нотификаций)
class EmailNotifier implements ObserverInterface {
    public function update(SubjectInterface $subject): void {
        if ($subject instanceof OrderService && $subject->getStatus() === 'shipped') {
            echo "Отправляем email клиенту: ваш заказ отправлен.n";
        }
    }
}

// Использование
$order = new OrderService();
$order->attach(new EmailNotifier());
$order->setStatus('shipped'); // Автоматически вызовет отправку email

Где я применял: В одном из проектов на Laravel мы использовали этот паттерн через встроенную систему событий (Event / Listener), которая является его реализацией, для отправки уведомлений, логирования и обновления кэша при изменении данных.

Ответ 18+ 🔞

Слушай, а вот Observer — это такая штука, которая тебе жизнь упрощает, когда нужно, чтобы одна часть кода нахуярила всем остальным, что что-то поменялось. Представь: у тебя есть главный объект, типа начальника цеха (это субъект), и куча подчинённых, которые сидят и ждут, когда он крикнет «Работаем!» (это наблюдатели). Так вот, паттерн этот — и есть тот самый крик, только автоматический. Начальнику похуй, кто конкретно его услышит — главное, что все, кто подписался, получат пизды... то есть, уведомление.

Зачем это, спрашивается? А затем, чтобы не превращать код в спагетти, где каждый модуль лезет в жопу другому. Субъект знает только, что есть какие-то наблюдатели с общим интерфейсом. А уж кто там конкретно — сервис нотификаций, логгер или система, которая при каждом изменении статуса заказа рисует тебе анимацию с единорогом — ему абсолютно похуй. Слабая связанность, ёпта! Добавляй новых подписчиков — не перелопачивай весь код субъекта.

Смотри, как это выглядит в коде, на примере системы заказов:

// Интерфейс Наблюдателя — типа договор: «чувак, у тебя будет метод update»
interface ObserverInterface {
    public function update(SubjectInterface $subject): void;
}

// Интерфейс Субъекта — договор: «я умею прикреплять, откреплять и орать на всех»
interface SubjectInterface {
    public function attach(ObserverInterface $observer): void;
    public function detach(ObserverInterface $observer): void;
    public function notify(): void;
}

// А вот и наш конкретный начальник — сервис заказов
class OrderService implements SubjectInterface {
    private $observers = []; // Здесь он хранит всех подписчиков
    private $orderStatus; // А это его внутреннее состояние, которое всех волнует

    // Подписать нового паникёра
    public function attach(ObserverInterface $observer): void {
        $this->observers[] = $observer;
    }

    // Оповестить всех — вот тут он и орёт «Внимание!»
    public function notify(): void {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    // Меняем статус заказа — и сразу трём всем подписчикам
    public function setStatus(string $status): void {
        $this->orderStatus = $status;
        $this->notify(); // Магия тут! Все узнают об изменении автоматом.
    }

    public function getStatus(): string {
        return $this->orderStatus;
    }
}

// А это один из подписчиков — сервис отправки email. Сидит, бздит, ждёт своего часа.
class EmailNotifier implements ObserverInterface {
    public function update(SubjectInterface $subject): void {
        // Проверяем, это наш начальник? И статус подходящий?
        if ($subject instanceof OrderService && $subject->getStatus() === 'shipped') {
            echo "Отправляем email клиенту: ваш заказ отправлен.n";
        }
    }
}

// Ну и демонстрация, как это всё вместе работает
$order = new OrderService();
$order->attach(new EmailNotifier()); // Подписываем email-нотифаер
$order->setStatus('shipped'); // Меняем статус — и email улетает сам, ядрёна вошь!

Где я это применял на практике? Да почти везде, где нужно было развязать узлы. Например, в одном проекте на Laravel его встроенная система событий (Event / Listener) — это и есть Observer на стероидах. Меняется запись в базе — ёперный театр, летит событие. А на него уже подписаны: листнер для отправки смс, листнер для записи в лог, листнер для сброса кэша и ещё какой-нибудь листнер, который при каждом обновлении заказа слал мне телеграм-сообщение «Опять работаем, блядь». И главное — сервису заказов абсолютно похуй, кто и что делает. Его дело — крикнуть. А уж кто как отреагирует — это их проблемы. Удобно, ни хуя себе!