Observer mode (observer mode) --- behavioral mode

Observer mode should be one of the most widely used and influential modes, because an instance of Observer Model/View/Control (MVC) structure has a very important position and significance in the system development architecture design. MVC implements business logic Decoupling from the presentation layer. I personally think that the  Observer  mode is one of the modes that must be mastered and used in the software development process  . In MFC, Doc/View (document view structure) provides a framework structure to implement MVC (there is an article analyzing Doc/View from the perspective of design pattern (Observer pattern) is being further written, unfortunately time:) ). In the Java lineup, Struts provides a framework for implementing MVC similar to the Doc/View structure in MFC. In addition, the Java language itself provides an implementation interface for the Observer pattern, which will be given in the discussion.

        Of course, MVC is just an example of the Observer pattern. The problem to be solved by the Observer model is to establish a one (Subject)-to-many (Observer) dependency, and make sure that when the "one" changes, the many that rely on the "one" can also change simultaneously. The most common example is: when performing statistical analysis on the same set of data, we hope to provide multiple forms of representation (for example, statistical display in a table, statistical display of histogram, percentage statistical display, etc.). These representations all depend on the same set of data. Of course, when the data changes, all the statistical displays can be changed at the same time. The Observer model solves this problem.

         The target Subject here provides the registration (Attach) and deregistration (Detach) operations of the observer Observer that depends on it, and provides the operation (Notify) to synchronize all the observers that depend on it. The observer Observer provides an Update operation. Note that the Update operation of the Observer here does not update itself when the Observer changes the state of the Subject. This update operation will be delayed until the Subject sends a Notify to notify all Observers to modify (call Update ).

1. What is the observer pattern

In many designs, multiple objects are often interested in data changes in a particular object, and these multiple objects all want to track the data changes in that particular object, that is, when there is a one-to-many relationship between the objects , In this case, you can use the observer mode. When an object is modified, its dependent objects will be notified automatically.

The observer mode is a mature mode in which multiple objects want to know the data changes in an object. In the observer mode, there is an object called "theme" and several objects called "observers". There is a one-to-many dependency relationship between "theme" and "observer", when the state of "theme" When a change occurs, all "observers" are notified.

The main solution: the problem of notifying other objects when the state of an object changes, and the ease of use and low coupling must be considered to ensure a high degree of collaboration.

Second, the structure of the observer model

 There are four roles in the structure of the observer pattern:

(1) Subject (Subject) : The subject is an interface that specifies the methods that a specific subject needs to implement, such as methods for adding and deleting observers and notifying observers to update data.

(2) Observer : Observer is an interface that specifies the method used by specific observers to update data.

(3) Concrete Subject (ConcreteSubject) : A concrete subject is an instance of a class that implements the subject interface, which contains data that can change frequently. Specific topics need to use a collection, such as ArrayList, to store observer references, so that specific observers can be notified when data changes.

(4) Concrete Observer (ConcreteObserver) : Concrete Observer is an instance of the class that implements the Observer interface. The specific observer contains theme interface variables that can store the specific theme reference, so that the specific observer can let the specific theme add its own reference to the collection of the specific theme, make itself its observer, or let the specific theme change itself from Remove from the collection of specific topics so that you are no longer an observer of it.

Three, the use of the observer mode

(1) When an object's data is updated, other objects need to be notified, but this object does not want to be tightly coupled with the notified objects.

(2) When the data of an object is updated, this object needs to let other objects also update their own data, but this object does not know how many objects need to update the data.

The observer mode is very common in actual project applications. For example, if you withdraw money from an ATM machine and enter the wrong password many times, the card will be swallowed by the ATM. What events will be triggered when the swallowing action occurs? The first camera takes continuous snapshots. Second, it informs the monitoring system that the card swallowing occurs. Third, initializes the ATM screen and returns to the initial state. You can’t swallow a card just because the entire ATM can’t be used, right? Both actions are accomplished through the observer mode. Observers can broadcast messages, and one message can trigger multiple events. This is a very important function of the observer mode.

There are also two key issues to be solved when using the observer mode: 

The problem of the broadcast chain .

If you have done database triggers, you should know that there is a trigger chain problem. For example, a trigger is written on table A, and the content is to update a piece of data in table B after a field is updated, and table B also has a Trigger, to update table C, table C also has triggers... It's over, this database is basically destroyed! Our observer model has the same problem. An observer can have dual identities. Even the observer is also the observed. This is no problem, but once the chain is established, the logic is more complicated and the maintainability is very poor. According to Experience suggests that at most one object in an observer mode is both the observer and the observed, that is to say, the message is forwarded at most once (transmitted twice), which is better to control;


Process issues asynchronously .

The observer has an action, and the observer has to respond. What if there are more observers and the processing time is longer? Then use asynchronous. Asynchronous processing must consider the issues of thread safety and queues. If you have time to look at Message Queue, you will have a deeper understanding.

Fourth, the advantages and disadvantages of the observer model

advantage:

  1. The specific subject and the specific observer are loosely coupled. Since the theme interface only depends on the observer interface, the specific theme only knows that its observer is an instance of a certain class that implements the observer interface, but does not need to know which class it is. Similarly, because the observer only depends on the theme interface, the specific observer only knows that the theme it depends on is an instance of a certain class that implements the theme interface, but does not need to know which class it is.

  2. The observer mode satisfies the "open-close principle". The theme interface only depends on the observer interface. In this way, the class that creates a specific theme depends only on the observer interface. Therefore, if you add a new class that implements the observer interface, you do not need to modify the code of the class that creates the specific theme. . . Similarly, the class that creates a specific observer depends only on the theme interface. If you add a new class that implements the theme interface, you don't need to modify the code for creating a specific observer class.

Disadvantages: 

  1. If an observed object has many direct and indirect observers, it will take a lot of time to notify all observers.

  2. If there is a circular dependency between the observer and the observation target, the observation target will trigger a cyclic call between them, which may cause the system to crash.

  3. The observer mode does not have a corresponding mechanism to let the observer know how the observed target has changed, but only knows that the observed target has changed.

Five, the realization of the observer mode

Observer class---abstract observer , defines an interface for all specific observers, and updates itself when notified by the subject.

This interface is called an update interface, and abstract observers are generally implemented by an abstract class or an interface. The update interface usually includes an Update method, which is called the update method.

#pragma once

//Subject.h

#include <list>

#include <string>

using namespace std;

typedef string State;

class Observer;

class Subject

{

public:

virtual ~Subject();

virtual void Attach(Observer* obv);

virtual void Detach(Observer* obv);

virtual void Notify();

virtual void SetState(const State& st) = 0;

virtual State GetState() = 0; protected:

Subject();

private:

list<Observer* >* _obvs;

};

class ConcreteSubject:public Subject

{

public:

ConcreteSubject();

~ConcreteSubject();

State GetState();

void SetState(const State& st);

protected:

private:

State _st;

};


//Subject.cpp

#include "stdafx.h"

#include "Subject.h"

#include "Observer.h"

#include <iostream>

#include <list>

using namespace std;

typedef string state;

Subject::Subject()

{

//****在模板的使用之前一定要 new,创建

_obvs = new list<Observer*>;

}

Subject::~Subject()

{

}

void Subject::Attach(Observer* obv)

{

_obvs->push_front(obv);

}

void Subject::Detach(Observer* obv)

{

if (obv != NULL)

_obvs->remove(obv);

}

void Subject::Notify()

{

list<Observer*>::iterator it;

it = _obvs->begin();

for (;it != _obvs->end();it++)

{

//关于模板和 iterator 的用法

(*it)->Update(this);

}

}

ConcreteSubject::ConcreteSubject()

{

_st = '\0';

}

ConcreteSubject::~ConcreteSubject()

{

}

State ConcreteSubject::GetState()

{

return _st;

}

void ConcreteSubject::SetState(const State& st)

{

_st = st;

}


#pragma once

//Observer.h

#include "Subject.h"

#include <string>

using namespace std;

typedef string State;

class Observer

{

public:

virtual ~Observer();

virtual void Update(Subject* sub) = 0;

virtual void PrintInfo() = 0;

protected:

Observer();

State _st;

private:

};

class ConcreteObserverA:public Observer

{

public:

virtual Subject* GetSubject();

ConcreteObserverA(Subject* sub);

virtual ~ConcreteObserverA();

//传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。

void Update(Subject* sub);

void PrintInfo();

protected:

private:

Subject* _sub;

};

class ConcreteObserverB:public Observer

{

public:

virtual Subject* GetSubject();

ConcreteObserverB(Subject* sub);

virtual ~ConcreteObserverB();

//传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。

void Update(Subject* sub);

void PrintInfo();

protected:

private:

Subject* _sub;

};


//Observer.cpp

#include "stdafx.h"

#include "Observer.h"

#include "Subject.h"

#include <iostream>

#include <string>

using namespace std;

Observer::Observer()

{

_st = '\0';

}

Observer::~Observer()

{

}

ConcreteObserverA::ConcreteObserverA(Subject* sub)

{

_sub = sub;

_sub->Attach(this);

}

ConcreteObserverA::~ConcreteObserverA()

{

_sub->Detach(this);

if (_sub != 0)

{

delete _sub;

}

}

Subject* ConcreteObserverA::GetSubject()

{

return _sub;

}

void ConcreteObserverA::PrintInfo()

{

cout<<"ConcreteObserverA observer.... "<<_sub->GetState()<<endl;

}

void ConcreteObserverA::Update(Subject* sub)

{

_st = sub->GetState();

PrintInfo();

}

ConcreteObserverB::ConcreteObserverB(Subject* sub)

{

_sub = sub;

_sub->Attach(this);

}

ConcreteObserverB::~ConcreteObserverB()

{

_sub->Detach(this);

if (_sub != 0)

{

delete _sub;

}

}

Subject* ConcreteObserverB::GetSubject()

{

return _sub;

}

void ConcreteObserverB::PrintInfo()

{

cout<<"ConcreteObserverB observer.... "<<_sub->GetState()<<endl;

}

void ConcreteObserverB::Update(Subject* sub)

{

_st = sub->GetState();

PrintInfo();

}


int main(int argc, _TCHAR* argv[])

{

ConcreteSubject* sub = new ConcreteSubject();

Observer* o1 = new ConcreteObserverA(sub);

Observer* o2 = new ConcreteObserverB(sub);

sub->SetState("old");

sub->Notify();

sub->SetState("new"); //也可以由 Observer 调用

sub->Notify();

return 0;

}

       In the implementation of the Observer pattern, Subject maintains a list as a container for storing all its observers. Whenever the Notify operation is called, it traverses the Observer objects in the list and broadcasts notifications to change the state (calling the Update operation of the Observer). The state of the target can be changed by the Subject itself (example), or it can be changed by an operation of the Observer (the SetState operation of the Subject can be called). The Notify operation can be actively broadcast by the Subject target (example), or it can be called by the Observer (because the Observer maintains a pointer to the Subject).

         Running the sample program, you can see that when the subject is in the state "old", the two observers that depend on it all display "old", and when the target state changes to "new", the two observers that depend on it also Are changed to "new".     

Six, the combination of observer mode and commission

Although the above code has used the dependency inversion principle, the "abstract informer" still relies on the "abstract observer", that is, if there is no such interface as the abstract observer, the notification function cannot be sent.
The other is for each specific observer, which is not necessarily an Update method call.
Purpose: The notifier and the observer do not know each other at all, and the client decides who to notify

 

Seven, summary

When implementing the observer mode, pay attention to that the interactive relationship between the observer and the observed object cannot be embodied as a direct call between the classes, otherwise it will make the observer and the observed object be closely coupled, fundamentally Violates the principles of object-oriented design . Whether the observer "observes" the observed object, or the observer "notifies" the observer of his own changes, it should not be called directly

Guess you like

Origin blog.csdn.net/weixin_41882459/article/details/112688096