Паттерн 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 можно без усилий, не изменяя существующий код.
Задание
ЛегкоДавайте создадим систему оповещений об изменении цены акции с использованием шаблона 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
update()каждого observer, передавая ему имя акции и новую цену.- Хранить имя акции и текущую цену (используйте продвижение свойств constructor для имени, инициализируйте цену значением
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, чтобы вызвать уведомления
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Повторение — простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыПовторение — иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateМодификаторы доступа подробноГеттеры и сеттерыСокрытие информацииПродвижение конструктора (8.0)Повторение — система учёта студентов2Пространства имён и автозагрузка
Введение в пространства имёнКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerПовторение — структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс и абстрактный классАннотация типов с интерфейсамиПовторение — калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeПовторение — пользовательская коллекция3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищённые свойстваСвойства только для чтения (PHP 8.1)Повторение — менеджер банковского счёта6Полиморфизм
Повторение переопределения методовПолиморфизм через интерфейсыАннотации типов и объединённые типыПозднее статическое связываниеПовторение — обработчик платежей9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования, часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн StrategyПотренируйтесь самостоятельно: Онлайн-компилятор PHP