Инструменты пользователя

Инструменты сайта


php:shablony_proektirovanija:behavioral_patterns:observer

Наблюдатель (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);
php/shablony_proektirovanija/behavioral_patterns/observer.txt · Последние изменения: 2023/08/07 23:23 — werwolf