=====Наблюдатель (Observer)=====
Наблюдатель (англ. Observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents). Реализует у класса механизм, который позволяет объекту этого класса получать оповещения об изменении состояния других объектов и тем самым наблюдать за ними.\\
\\
Классы, на события которых другие классы подписываются, называются субъектами (Subjects), а подписывающиеся классы называются наблюдателями (Observers). \\
\\
В PHP осуществляется встроенная поддержка этого шаблона через входящее в поставку\\
расширение SPL (Standard PHP Library):
* **SplObserver** - интерфейс для Observer (наблюдателя),
* **SplSubject** - интерфейс Observable (наблюдаемого),
* **SplObjectStorage** - вспомогательный класс (обеспечивает улучшенное сохранение и удаление объектов, в частности, реализованы методы attach() и detach()).
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.
====без использования spl библиотеки====
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);