Menu
Coddy logo textTech

Паттерн Observer

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

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

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

Вот базовая реализация на 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!

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

challenge icon

Задание

Легко

Давайте создадим систему оповещения о ценах на акции, используя паттерн Наблюдатель (Observer Pattern). Когда цена акции меняется, все зарегистрированные инвесторы должны получать уведомления автоматически — это идеальное практическое применение данного паттерна.

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

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

    Создайте объект Stock с заданным именем. Подпишите EmailInvestor с адресом электронной почты и SmsInvestor с номером телефона.

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

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

Шпаргалка

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

Ключевые компоненты:

  • Интерфейс Наблюдателя (Observer Interface) — определяет контракт для наблюдателей с методом update()
  • Субъект (Subject) — хранит список наблюдателей и уведомляет их об изменениях состояния
  • Конкретные наблюдатели (Concrete Observers) — реализуют интерфейс Наблюдателя для получения уведомлений

Базовая реализация:

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

class Subject {
    private array $observers = [];
    
    public function subscribe(Observer $observer): void {
        $this->observers[] = $observer;
    }
    
    public function notify(string $data): void {
        foreach ($this->observers as $observer) {
            $observer->update($data);
        }
    }
}

class ConcreteObserver implements Observer {
    public function __construct(private string $name) {}
    
    public function update(string $message): void {
        echo "Notifying {$this->name}: $message\n";
    }
}

$subject = new Subject();
$subject->subscribe(new ConcreteObserver("Observer1"));
$subject->subscribe(new ConcreteObserver("Observer2"));
$subject->notify("State changed!");

Преимущества:

  • Обеспечивает слабую связанность субъекта и его наблюдателей
  • Легко добавлять новые типы наблюдателей без изменения существующего кода
  • Поддерживает динамическую подписку/отписку

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

<?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: Установить цену акции на 150.50, чтобы вызвать уведомления
?>
quiz iconПроверьте себя

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

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