Оглавление:
Карта сайта:
Оглавление:
Карта сайта:
Наблюдатель (англ. Observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents). Реализует у класса механизм, который позволяет объекту этого класса получать оповещения об изменении состояния других объектов и тем самым наблюдать за ними.
Классы, на события которых другие классы подписываются, называются субъектами (Subjects), а подписывающиеся классы называются наблюдателями (Observers).
В PHP осуществляется встроенная поддержка этого шаблона через входящее в поставку
расширение SPL (Standard PHP Library):
namespace RefactoringGuru\Observer\Conceptual; /** * PHP имеет несколько встроенных интерфейсов, связанных с паттерном * Наблюдатель. * * Вот как выглядит интерфейс Издателя: * * @link http://php.net/manual/ru/class.splsubject.php * * interface SplSubject * { * // Присоединяет наблюдателя к издателю. * public function attach(SplObserver $observer); * * // Отсоединяет наблюдателя от издателя. * public function detach(SplObserver $observer); * * // Уведомляет всех наблюдателей о событии. * public function notify(); * } * * Также имеется встроенный интерфейс для Наблюдателей: * * @link http://php.net/manual/ru/class.splobserver.php * * interface SplObserver * { * public function update(SplSubject $subject); * } */ /** * Издатель владеет некоторым важным состоянием и оповещает наблюдателей о его * изменениях. */ class Subject implements \SplSubject { /** * @var int Для удобства в этой переменной хранится состояние Издателя, * необходимое всем подписчикам. */ public $state; /** * @var \SplObjectStorage Список подписчиков. В реальной жизни список * подписчиков может храниться в более подробном виде (классифицируется по * типу события и т.д.) */ private $observers; public function __construct() { $this->observers = new \SplObjectStorage(); } /** * Методы управления подпиской. */ public function attach(\SplObserver $observer): void { echo "Subject: Attached an observer.\n"; $this->observers->attach($observer); } public function detach(\SplObserver $observer): void { $this->observers->detach($observer); echo "Subject: Detached an observer.\n"; } /** * Запуск обновления в каждом подписчике. */ public function notify(): void { echo "Subject: Notifying observers...\n"; foreach ($this->observers as $observer) { $observer->update($this); } } /** * Обычно логика подписки – только часть того, что делает Издатель. Издатели * часто содержат некоторую важную бизнес-логику, которая запускает метод * уведомления всякий раз, когда должно произойти что-то важное (или после * этого). */ public function someBusinessLogic(): void { echo "\nSubject: I'm doing something important.\n"; $this->state = rand(0, 10); echo "Subject: My state has just changed to: {$this->state}\n"; $this->notify(); } } /** * Конкретные Наблюдатели реагируют на обновления, выпущенные Издателем, к * которому они прикреплены. */ class ConcreteObserverA implements \SplObserver { public function update(\SplSubject $subject): void { if ($subject->state < 3) { echo "ConcreteObserverA: Reacted to the event.\n"; } } } class ConcreteObserverB implements \SplObserver { public function update(\SplSubject $subject): void { if ($subject->state == 0 || $subject->state >= 2) { echo "ConcreteObserverB: Reacted to the event.\n"; } } } /** * Клиентский код. */ $subject = new Subject(); $o1 = new ConcreteObserverA(); $subject->attach($o1); $o2 = new ConcreteObserverB(); $subject->attach($o2); $subject->someBusinessLogic(); $subject->someBusinessLogic(); $subject->detach($o2); $subject->someBusinessLogic();
Результат выполнения:
Subject: Attached an observer. Subject: Attached an observer. Subject: I'm doing something important. Subject: My state has just changed to: 2 Subject: Notifying observers... ConcreteObserverA: Reacted to the event. ConcreteObserverB: Reacted to the event. Subject: I'm doing something important. Subject: My state has just changed to: 4 Subject: Notifying observers... ConcreteObserverB: Reacted to the event. Subject: Detached an observer. Subject: I'm doing something important. Subject: My state has just changed to: 1 Subject: Notifying observers... ConcreteObserverA: Reacted to the event.
interface Observer { function notify($obj); } class ExchangeRate { static private $instance = NULL; private $observers = array(); private $exchange_rate; private function __construct() {} private function __clone() {} static public function getInstance() { if(self::$instance == NULL) { self::$instance = new ExchangeRate(); } return self::$instance; } public function getExchangeRate() { return $this->exchange_rate; } public function setExchangeRate($new_rate) { $this->exchange_rate = $new_rate; $this->notifyObservers(); } public function registerObserver(Observer $obj) { $this->observers[] = $obj; } function notifyObservers() { foreach($this->observers as $obj) { $obj->notify($this); } } } class ProductItem implements Observer { public function __construct() { ExchangeRate::getInstance()->registerObserver($this); } public function notify($obj) { if($obj instanceof ExchangeRate) { // Update exchange rate data print "Received update!\n"; } } } $product1 = new ProductItem(); $product2 = new ProductItem(); ExchangeRate::getInstance()->setExchangeRate(4.5);