Паттерн 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. Это позволяет легко добавлять новые типы наблюдателей без изменения существующего кода.
Задание
ЛегкоДавайте создадим систему оповещения о ценах на акции, используя паттерн Наблюдатель (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, чтобы вызвать уведомления
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy