Menu
Coddy logo textTech

Паттерн Observer

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 73 из 91.

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

Представьте подписку на newsletter. newsletter — это субъект, а подписчики — наблюдатели. Когда публикуется новый выпуск, все подписчики получают его автоматически, и newsletter не нужно знать подробности о каждом подписчике.

Вот базовая реализация на PHP:

<?php
interface Observer {
    public function update(string $message): void;
}

class Newsletter {
    private array $subscribers = [];
    
    public function subscribe(Observer $observer): void {
        $this->subscribers[] = $observer;
    }
    
    public function publish(string $content): void {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->update($content);
        }
    }
}

class EmailSubscriber implements Observer {
    public function __construct(private string $email) {}
    
    public function update(string $message): void {
        echo "Sending to {$this->email}: $message\n";
    }
}

$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");

Вывод:

Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!

Этот шаблон отделяет subject от его observers. Классу Newsletter не нужно знать, уведомляет ли он подписчиков по электронной почте, подписчиков по SMS или сервисы push-уведомлений. Он просто вызывает update() у всего, что реализует интерфейс Observer. Благодаря этому добавлять новые типы observers можно без усилий, не изменяя существующий код.

challenge icon

Задание

Легко

Давайте создадим систему оповещений об изменении цены акции с использованием шаблона Observer. Когда цена акции изменяется, все зарегистрированные Investors должны получать уведомление автоматически: это идеальное практическое применение данного шаблона.

Вы организуете код в четырёх файлах:

  • Observer.php: Определите интерфейс Observer с единственным методом update(string $stockName, float $price): void. Это контракт, которому должны следовать все observers, чтобы получать notifications.
  • Investors.php: Подключите файл Observer и создайте два класса, которые реализуют интерфейс:
    • EmailInvestor: Принимает адрес email в своём constructor (используйте продвижение свойств constructor). Его метод update() выводит "Email to [email]: [stockName] is now $[price]"
    • SmsInvestor: Принимает номер phone в своём constructor. Его метод update() выводит "SMS to [phone]: [stockName] is now $[price]"
  • Stock.php: Подключите файл Observer и создайте класс Stock, который выступает в роли субъекта. Ваш Stock должен:
    • Хранить имя акции и текущую цену (используйте продвижение свойств constructor для имени, инициализируйте цену значением 0.0)
    • Поддерживать закрытый массив observers
    • Иметь метод subscribe(Observer $observer): void для добавления observers
    • Иметь метод setPrice(float $price): void, который обновляет цену и уведомляет всех observers
    При уведомлении observers вызывайте метод update() каждого observer, передавая ему имя акции и новую цену.
  • main.php: Подключите файлы Investors и Stock. Вы получите три входных значения: имя акции, адрес email и номер phone.

    Создайте Stock с указанным именем. Подпишите EmailInvestor с адресом email и SmsInvestor с номером phone.

    Установите цену акции равной 150.50, чтобы вызвать notifications для всех подписчиков.

Шаблон Observer сохраняет слабую связанность класса Stock с конкретными методами уведомления. Позже вы легко сможете добавить PushInvestor или SlackInvestor, не изменяя класс Stock вообще.

Попробуйте сами

<?php
require_once 'Investors.php';
require_once 'Stock.php';

// Считать входные данные
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));

// TODO: Создать Stock с заданным именем

// TODO: Подписать EmailInvestor с адресом электронной почты

// TODO: Подписать SmsInvestor с номером телефона

// TODO: Установить цену stock в 150.50, чтобы вызвать уведомления
?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP