Использование паттерна Observer

1. Проведение различия между основной (или независимой) и дополнительной (или зависимой) функциональностями.

2. Моделирование "независимую" функциональность с помощью абстракции "субъект".

3. Моделирование "зависимую" функциональность с помощью иерархии "наблюдатель".

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

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

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

7. Subject извещает всех зарегистрированных наблюдателей.

8. Subject может "протолкнуть" информацию в наблюдателей, или наблюдатели могут "вытянуть" необходимую им информацию от объекта Subject.

 

 

#include <iostream> #include <vector> 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; notify(); } int getVal() { return value; } void notify(); }; // 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(); cout << v << " div " << d << " is " << v/d << '\n'; } }; class ModObserver: public Observer { public: ModObserver(Subject *mod, int div): Observer(mod, div){} void update() { int v = getSubject()->getVal(), d = getDivisor(); cout << v << " mod " << d << " is " << v%d << '\n'; } }; int main() { Subject subj; DivObserver divObs1(&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

#include <iostream>

#include <string>

#include <list>

using namespace std;

class Supervised String;

Class IObserver

{

public:

virtual void handleEvent(const SupervisedString&) = 0;

};

Class SupervisedString // Observable class

{

string _str;

list<IObserver* const> _observers;

void _Notify()

{

   for(auto iter: _observers)

   {

       iter->handleEvent(*this);

   }

}

public:

void add(IObserver& ref)

{

   _observers.push_back(&ref);

}

void remove(IObserver& ref)

{

   _observers.remove(&ref);

}

const string& get() const

{

   return _str;

}

void reset(string str)

{

   _str = str;

   _Notify();

}

};

class Reflector: public IObserver // Prints the observed string into cout

{

public:

virtual void handleEvent(const SupervisedString& ref)

{

   cout << ref.get() << endl;

}

};

Class Counter: public IObserver

// Prints the length of observed string into cout

{

public:

virtual void handleEvent(const SupervisedString& ref)

{

cout << "length = " << ref.get().length() << endl;

}

};

Int main()

{

SupervisedString str;

Reflector refl;

Counter cnt;

str.add(refl);

str.reset("Hello, World!");

cout << endl;

str.remove(refl);

str.add(cnt);

str.reset("World, Hello!");

cout << endl;

return 0;

}

Рассмотрим еще раз саму идею паттерна, его применение и реализацию.

Наблюдатель – поведенческий шаблон проектирования.

Данный шаблон проектирования встречается под именем “подчиненные” (Dependents), “издатель-подписчик” (Publisher-Subscriber).

Смысл данного паттерна заключается в том, что  если объект обновляется, то все его зависимые объекты обновляются так же.

Рассмотрим случай, в которых может быть применен, если система обладает следующими свойствами:

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

Диаграмма классов представлена ниже.

 

Основными элементами в данном паттерне являются объект наблюдения (Subject) и реализация различных наблюдателей (extends Observer).

Будем хранить всех наблюдателей объекта непосредственно в нем.

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

Абстрактный класс Observer содержит абстрактный метод update, который реализует каждый из конкретных наблюдателей в зависимости от своих целей.

Так же класс Observer содержит ссылку на субъект, за которым он наблюдает.

Реализация представлена ниже:

 

package edu.patterns.observer.observers.impl;

import edu.patterns.observer.observers.Observer;

import edu.patterns.observer.subjects.Subject;

public abstract class Observer {

protected Subject object;

public abstract void update();

}

Тут все довольно просто и нет необходимости пояснять.

Далее реализуем классы учителя и родителя соответственно.

 

public class Teacher extends Observer {

public Teacher(Subject object){

this.object = object;

this.object.attach(this);

}

@Override

public void update() {

System.out.println("Teacher is aware of your results:" + this.object.getState());

}

}

И класс родителей наблюдателей:

 

public class Parents extends Observer{

public Parents(Subject object){

this.object = object;

this.object.attach(this);

}

@Override

public void update() {

System.out.println("Parents are aware of your results:" + this.object.getState());

}

}

Классы Teacher и Parents практически одинаковы.

Тут стоит заметить, что экземпляр данного класса позволяет следить за 1 объектом и привязка осуществляется в конструкторе с помощью метода attach.

Когда ученик получит новую оценка учитель и родители непременно об этом узнают.

А теперь рассмотрим реализация самого субъекта.

 

public class Subject {

private List<Observer> observers = new ArrayList<Observer>();

private int state;

public int getState() {

return state;

}

public void setState(int state) {

this.state = state;

notifyAllObservers();

}

public void attach(Observer observer){

observers.add(observer);

}

private void notifyAllObservers(){

for (Observer observer: observers){

observer.update();

}

}

}

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

Можно было бы написать код, который позволял бы удалять смотрителей, но мы просто рассмотрели саму идею.

Все основные класс записаны и теперь остается только это все запустить, класс ниже, позволит это сделать.

public class ObserverRunner {

public static void main(String args[]){

Subject pupil = new Subject();

new Parents(pupil);

new Teacher(pupil);

System.out.println("You've received new mark: 5");

pupil.setState(5);

}

}

Вы можете запустить данный пример и посмотреть, что любые полученные оценки у ученика не остаются без внимания.

Теперь, когда мы реализовали данный поведенческий паттерн сами, хочу сказать, что начиная с JDK 1.0 пакет java.utils имеет аналогичные классы и интерфейсы (Observer, Observable) для реализации данного поведения, подробнее о них можно почитать по ссылкам ниже:

https://docs.oracle.com/javase/7/docs/api/java/util/Observer.html

http://docs.oracle.com/javase/7/docs/api/java/util/Observable.html

Команда (Command)


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: