НЕ МОЛЧИ!!!    Сделай что-нибудь, чтобы остановить войну России в Украине.
...бойтесь людей равнодушных - именно с их молчаливого согласия происходят все самые ужасные преступления на свете.   ("Репортаж с петлёй на шее")

Шаблон проектирования Observer(Наблюдатель)

Рассматривается поведенческий шаблон проектирования Observer, приведен пример реализации.

Содержание

  • Применение
  • Реализация
  • Итоги

Применение

Сегодня рассмотрим достаточно простой, но важный поведенческий шаблон проектирования Observer. На примере магазина покажем, когда его можно применять. Для этого представьте, что вы хотите отслеживать наличие и стоимость товара в каком-либо продвинутом интернет магазине. Естественно, вы такой не один, причем состав таких подписчиков на товар ежедневно изменяется. Что мы имеем? Магазину желательно оповестить всех потенциальных покупателей об изменениях на складе, а вам надо иметь возможность подписаться или отписаться от оповещений. Чтобы реализовать подобное, магазин должен извещать заинтересованных пользователей об изменениях. А чтобы знать этих заинтересованных, надо дать им возможность подписываться и отписываться на товары. В этом случае магазин без проблем реализует подписку на свои товары. Вот, собственно, и вся идея шаблона Observer. Другими словами, объект должен обеспечить регистрацию наблюдателей у себя с тем, чтобы отсылать им уведомления о своих изменениях.

Реализация

В реализации шаблона будут использоваться такие компоненты.

  • интерфейс IObservable — определяет набор методов, с помощью которых подписчики могут управлять подпиской на товар;
  • Класс Goods — товар, в числе прочего реализует интерфейс IObservable;
  • класс Customer — определяет функционал покупателя и выступает в роли наблюдателя.

Начнем с нашего магазина. Вначале определим его интерфейс для реализации возможности подписки.

interface IObservable
{
    function registerObserver(Customer $customer);
    function removeObserver(Customer $customer);
    function notifyObservers();
}

Для упрощения примера приведенный выше интерфейс будет реализован в классе товара. В этом случае при каждом изменении своего состояния товар будет извещать об этом клиентов, которые подписаны на его отслеживание.

class Goods implements IObservable
{
    private $name;            //наименование товара

    private $event;           //состояние товара

    private $customers = [];  //массив подписчиков 

    public function __construct($name) 
    {
        $this->name = $name;
    }

    public function registerObserver(Customer $customer)
    {
        $this->customers[] = $customer;
    }

    public function removeObserver(Customer $customer)
    {
        unset($this->customers[$customer]);
    }

    public function notifyObservers($event = 'update')
    {
        foreach ($this->customers as $observer) {
            $observer->notify($this->name, $this->event);
        }
    }

    public function setEvent($event = 'update')
    {
        $this->event = $event;
        $this->notifyObservers($this->event);
    }
//  остальной функционал класса
}

Класс Customer в примере выступает в роли наблюдателя. Он должен отработать поступившее сообщение, в нашем примере он просто сообщает о получении извещения в методе notify().

Class Customer 
{
    public $name;

    public __construct(String $name)
    {
        $this->name = $name;
    }

    public notify(String $goods, String $event)
    {
        echo 'Клиент ' . $name . 'получил сообщение ' . $event . ' о товаре ' . $goods;
    }
}

Вот как это будет работать.

$goods = new Goods('Товар1');
$clent1 = new Customer('client1');
$clent2 = new Customer('client2');
$clent3 = new Customer('client3');
$goods->registerObserver($clent1);
$goods->registerObserver($clent2);
$goods->setEvent('event1');

При изменении состояния объекта $goods при вызове метода setEvent() будут оповещены все клиенты, которые зарегистрировались на отслеживание товара.

Итоги

Итак, мы рассмотрели шаблон проектирования Observer, который позволяет при изменении состояния одного объекта оповещать все зависящие от него компоненты приложения. С его помощью организуется взаимодействие типа «один ко многим» и позволяет избежать при этом избежать жесткой зависимости между взаимодействующими объектами.