Паттерн Observer (Наблюдатель)

Название и классификация паттерна

Наблюдатель — паттерн поведения объектов.

Назначение паттерна Observer

Паттерн Observer определяет зависимость «один-ко-многим» между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются и обновляются автоматически.

Паттерн Observer инкапсулирует главный (независимый) компонент в абстракцию Subject и изменяемые (зависимые) компоненты в иерархию Observer.

Паттерн Observer определяет часть «View» в модели Model—View-Controller (MVC).

Решаемая проблема

Имеется система, состоящая из множества взаимодействующих классов. При этом взаимодействующие объекты должны находиться в согласованных состояниях. Вы хотите избежать монолитности такой системы, сделав классы слабосвязанными (или повторно используемыми).

Обсуждение паттерна Observer

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

Паттерн Наблюдатель описывает, как устанавливать такие отношения. Паттерн Obsetyer определяет объект Subject, хранящий данные (модель), а всю функциональность «представлений» делегирует слабосвязанным отдельным объектам Observer. У субъекта может быть сколько угодно зависимых от него Наблюдателей. Все Наблюдатели уведомляются об изменениях в состоянии субъекта. Получив уведомление, Наблюдатель опрашивает субъекта, чтобы синхронизировать с ним свое состояние. Такого рода взаимодействие часто называется отношением издатель—подписчик. Субъект издает или публикует уведомления и рассылает их, даже не имея информации о том, какие объекты являются подписчиками. На получение уведомлений может подписаться неограниченное количество Наблюдателей.

При создании Наблюдатели Observer регистрируются у объекта Subject. Когда объект Subject изменяется, он извещает об этом всех зарегистрированных Наблюдателей. После этого каждый обозреватель запрашивает у объекта Subject ту часть состояния, которая необходима для отображения данных.

Такая схема позволяет динамически настраивать количество и «типы» представлений объектов.

Применимость

У абстракции есть два аспекта, один из которых зависит от другого. Инкапсуляции этих аспектов в разные объекты позволяют изменять и повторно использовать их независимо.

При модификации одного объекта требуется изменить другие и заранее неизвестно, сколько именно объектов нужно изменить.

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

Структура паттерна Observer

Subject представляет главную (независимую) абстракцию. Observer представляет изменяемую (зависимую) абстракцию. Субъект извещает Наблюдателей о своем изменении, на что каждый Наблюдатель может запросить состояние субъекта.

UML-диаграмма классов паттерна Observer

Структура паттерна Наблюдатель показана на рис. 70.

UML-диаграмма паттерна Наблюдатель

Рис. 70. UML-диаграмма паттерна Наблюдатель

Участники Subject — субъект:

  • • располагает информацией о своих Наблюдателях. За субъектом может «следить» любое число Наблюдателей;
  • • предоставляет интерфейс для присоединения и отделения Наблюдателей.

Observer — Наблюдатель: определяет интерфейс обновления для объектов, которые должны быть уведомлены об изменении субъекта. ConcreteSubject — конкретный субъект:

  • • сохраняет состояние, представляющее интерес для конкретного Наблюдателя ConcreteObserver;
  • • посылает информацию своим Наблюдателям, когда происходит изменение.

ConcreteObserver — конкретный Наблюдатель:

  • • хранит ссылку на объект класса ConcreteSubject;
  • • сохраняет данные, которые должны быть согласованы с данными субъекта;
  • • реализует интерфейс обновления, определенный в классе Observer, чтобы поддерживать согласованность с субъектом.

Пример паттерна Observer

Паттерн Observer определяет зависимость «один-ко-многим» между объектами так, что при изменении состояния одного объекта все зависящие от него объекты уведомляются и обновляются автоматически. Некоторые аукционы демонстрируют этот паттерн. Каждый участник имеет карточку с цифрами, которую он использует для обозначения предлагаемой цены (ставки). Ведущий аукциона (Subject) начинает торги и наблюдает, когда кто-нибудь поднимает карточку, предлагая новую, более высокую цену. Ведущий принимает заявку, о чем тут же извещает всех участников аукциона (Observers).

Реализация паттерна Observer по шагам

Смоделируйте «независимую» функциональность с помощью абстракции «субъект».

Смоделируйте «зависимую» функциональность с помощью иерархии «Наблюдатель».

Класс Subject связан только с базовым классом Observer.

Наблюдатели регистрируются у субъекта.

Субъект извещает всех зарегистрированных Наблюдателей.

Наблюдатели «вытягивают» необходимую им информацию от объекта Subject.

Клиент настраивает количество и типы Наблюдателей.

#include

#include

using namespace std;

// 1. "Независимая" функциональность class Subject {

// 3. Связь только базовым классом Observer vector < class Observer * > views; int value;

public:

void attach(Observer *obs) { views. push_back(obs);

}

void setVal(int val) { value = val; notifyO;

}

int getValQ { return value;

}

void notifyO;

// 2. "Зависимая" функциональность class Observer {

Subject *model; int denom; public:

Observer(Subject *mod, int div) { model = mod; denom = div;

// 4. Наблюдатели регистрируются у субъекта model->attach(this);

}

virtual void update() = 0; protected:

Subject *getSubject() { return model;

}

int getDivisor() { return denom;

}

};

void Subject::notify() {

// 5. Извещение Наблюдателей for (int i = 0; i < views.size(); i++) views[i]->update();

}

class DivObserver: public Observer { public:

DivObserver(Subject *mod, int div): Observer(mod, div){}

void update() {

// 6. "Вытягивание" интересующей информации int V = getSubject()->getVal(), d = getDivisor(); co?t << v << " div " << d << " is " << v/d << ' ';

}

};

class ModObserver: public Observer { public:

ModObserver(Subject *mod, int div): Observer(mod, div){} void updateO {

int v = getSubject()->getVal(), d = getDivisor(); cout << v << " mod " << d << " is " << v%d << ' ';

}

int main() {

Subject subj;

DivObserver divObsl (&subj, 4); // 7. Клиент настраивает число DivObserver divObs2(&subj, 3); // и типы Наблюдателей ModObserver modObs3(&subj, 3); subj.setVal(14);

}

Вывод программы:

14 div 4 is 3 14 div 3 is 4 14 mod 3 is 2

Особенности паттерна Observer

Паттерны Chain of Responsibility, Command, Mediator и Observer показывают, как можно разделить отправителей и получателей запросов с учетом своих особенностей. Chain of Responsibility передает запрос отправителя по цепочке потенциальных получателей. Command определяет связь «оправитель—получатель» с помощью подкласса. В Mediator отправитель и получатель ссылаются друг на друга косвенно, через объект-посредник. В паттерне Observer связь между отправителем и получателем получается слабой, при этом число получателей может конфигурироваться во время выполнения.

Mediator и Observer являются конкурирующими паттернами. Если Observer распределяет взаимодействие с помощью объектов «Наблюдатель» и «Субъект», то Mediator использует объект-посредник для инкапсуляции взаимодействия между другими объектами. Мы обнаружили, что легче сделать повторно используемыми Наблюдателей и Субъектов, чем Посредников.

Mediator может использовать Obsemer для динамической регистрации коллег и их взаимодействия с Посредником.

 
< Пред   СОДЕРЖАНИЕ     След >