C + + Design Pattern -- observer pattern

C + + Design Pattern -- observer pattern

The official account of WeChat: Xueba in kindergarten

catalogue

preface

The requirement of observer pattern is that object A (observer) is highly sensitive to some change of object B (observed), and needs to respond at the moment of B change. For example, the popular police in the news catch thieves. The police need to catch thieves when they reach out to commit crimes. In this example, the police is the observer and the thief is the observed. The police need to keep an eye on the thief's every move to ensure that they will not miss any moment. The observer in the program is slightly different from this real [observation]. The observer does not need to keep an eye on the observer (for example, A does not need to check the status of B every 1ms), but tells the observer by registering or subscribing: I need your so and so status, and you should notify me when it changes. Adopting such A passive observation method can not only save the resource consumption of repeatedly retrieving the state, but also get the highest feedback speed.

definition

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

Observer mode, also known as Publish/Subscribe mode, defines a one to many dependency between objects, so that whenever an object changes state, all objects that depend on it will be notified and updated automatically.
This pattern is behavioral.

Whether publish subscribe mode and observer mode can be equated, there are different opinions on the Internet.

View 1: Publisher in publish subscribe mode is Subject in Observer mode, and Subscriber is Observer. When publisher changes, it takes the initiative to notify Subscriber
View 2: in the publish subscribe mode, publishers do not directly notify subscribers. In other words, publishers and subscribers do not know each other, and they are completely uncoupled. Their communication is through a third party, that is, the Broker in the message queue

My view: apart from implementation, the most important thing is the purpose of the model. Obviously, both of them realize a one to many dependency between objects. When the state of an object changes, all objects that depend on it will be notified and updated automatically. From the purpose, the two are the same.

There are two categories (subject and observer) with four roles
As we can see from the above, this includes:

  • Abstract topic / abstract subject role: save all observer objects in a collection, and there can be any number of observers. Abstract topics provide an interface to add and delete observer objects
  • Specific subject / concrete subject role: this role stores the relevant status into the specific observer object and sends a notice to all registered observers when the internal status of the specific subject changes
  • Abstract observer role: it defines an update interface to notify itself when the subject / observer is updated
  • Concrete observer role: implement the update interface defined by the abstract observer, so as to notify you to update your status when you get the update of the subject / observed
    The UML class diagram is as follows:

Core: loose coupling design between interactive objects

Code example

Code key:
The observed object itself should contain a container to store the observer object. When the observed object itself changes, all the observer objects in the container should be notified to update automatically.
The observer object can be registered in the of the observed. After registration, it can detect the changes of the observed and receive the notification of the observed. Of course, the observer can also be cancelled and the monitoring of the observed can be stopped.

#include <bits/stdc++.h>

//
//Observer mode
//

class Observer;
//Abstract observer
class Subject {
public:
    Subject() : m_nState(0) {}

    virtual ~Subject() = default;

    virtual void Attach(const std::shared_ptr<Observer> pObserver) = 0;

    virtual void Detach(const std::shared_ptr<Observer> pObserver) = 0;

    virtual void Notify() = 0;

    virtual int GetState() { return m_nState; }

    void SetState(int state) {
        std::cout << "Subject updated !" << std::endl;
        m_nState = state;
    }

protected:
    std::list<std::shared_ptr<Observer>> m_pObserver_list;
    int m_nState;
};

//Abstract observer
class Observer {
public:
    virtual ~Observer() = default;

    Observer(const std::shared_ptr<Subject> pSubject, const std::string &name = "unknown")
            : m_pSubject(pSubject), m_strName(name) {}

    virtual void Update() = 0;

    virtual const std::string &name() { return m_strName; }

protected:
    std::shared_ptr<Subject> m_pSubject;
    std::string m_strName;
};

//Specific observer
class ConcreteSubject : public Subject {
public:
    void Attach(const std::shared_ptr<Observer> pObserver) override {
        auto iter = std::find(m_pObserver_list.begin(), m_pObserver_list.end(), pObserver);
        if (iter == m_pObserver_list.end()) {
            std::cout << "Attach observer" << pObserver->name() << std::endl;
            m_pObserver_list.emplace_back(pObserver);
        }

    }

    void Detach(const std::shared_ptr<Observer> pObserver) override {
        std::cout << "Detach observer" << pObserver->name() << std::endl;
        m_pObserver_list.remove(pObserver);
    }

    //Circular notification to all observers
    void Notify() override {
        auto it = m_pObserver_list.begin();
        while (it != m_pObserver_list.end()) {
            (*it++)->Update();
        }
    }
};


//Specific observer 1
class Observer1 : public Observer {
public:
    Observer1(const std::shared_ptr<Subject> pSubject, const std::string &name = "unknown")
            : Observer(pSubject, name) {}

    void Update() override {
        std::cout << "Observer1_" << m_strName << " get the update.New state is: "
                  << m_pSubject->GetState() << std::endl;
    }
};

//Specific observer 2
class Observer2 : public Observer {
public:
    Observer2(const std::shared_ptr<Subject> pSubject, const std::string &name = "unknown")
            : Observer(pSubject, name) {}

    void Update() override {
        std::cout << "Observer2_" << m_strName << " get the update.New state is: "
                  << m_pSubject->GetState() << std::endl;
    }
};


int main() {

    std::shared_ptr<Subject> pSubject = std::make_shared<ConcreteSubject>();// Create observer

    // Create observer
    std::shared_ptr<Observer> pObserver1_1 = std::make_shared<Observer1>(pSubject, "1");
    std::shared_ptr<Observer> pObserver1_2 = std::make_shared<Observer1>(pSubject, "2");
    std::shared_ptr<Observer> pObserver1_3 = std::make_shared<Observer1>(pSubject, "3");

    std::shared_ptr<Observer> pObserver2_4 = std::make_shared<Observer2>(pSubject, "4");
    std::shared_ptr<Observer> pObserver2_5 = std::make_shared<Observer2>(pSubject, "5");
    std::shared_ptr<Observer> pObserver2_6 = std::make_shared<Observer2>(pSubject, "6");

    // Registered observer
    pSubject->Attach(pObserver1_1);
    pSubject->Attach(pObserver1_2);
    pSubject->Attach(pObserver1_3);
    pSubject->Attach(pObserver2_4);
    pSubject->Attach(pObserver2_5);
    pSubject->Attach(pObserver2_6);

    pSubject->SetState(2);// Change state
    pSubject->Notify();

    std::cout << std::string(50, '-') << std::endl;

    // Log off observer
    pSubject->Detach(pObserver1_1);
    pSubject->Detach(pObserver1_2);

    pSubject->SetState(3);
    pSubject->Notify();

    return 0;
    //The operation results are as follows:
    //Attach observer1
    //Attach observer2
    //Attach observer3
    //Attach observer4
    //Attach observer5
    //Attach observer6
    //Subject updated !
    //Observer1_1 get the update.New state is: 2
    //Observer1_2 get the update.New state is: 2
    //Observer1_3 get the update.New state is: 2
    //Observer2_4 get the update.New state is: 2
    //Observer2_5 get the update.New state is: 2
    //Observer2_6 get the update.New state is: 2
    //--------------------------------------------------
    //Detach observer1
    //Detach observer2
    //Subject updated !
    //Observer1_3 get the update.New state is: 3
    //Observer2_4 get the update.New state is: 3
    //Observer2_5 get the update.New state is: 3
    //Observer2_6 get the update.New state is: 3

}

summary

Observer model and mediation model

The intentions of the two models are very similar:
1. All belong to behavioral mode
2. All to deal with one to many relationship
3. The UML implementation is basically the same. There are collections to manage the collection of business objects and circular notification methods, which comply with the principle of single responsibility.

but there is a big difference between the two:
There are two roles in the mediation mode: mediator and collage. Generally, mediator does not actively initiate events to notify collage, while collage has the ability to send and receive messages. Mediator is generally neither the source nor the destination of message transmission. It acts as a transit station. Collague can be both the initiator of the message and the receiver of the message transmission, which means that the message is bidirectional. There can be more than one collage. Mediators emphasize the interaction between collage classes.
In contrast to the Observer mode, there is only one message initiator, that is, subject. All observers pay attention to the message of subject. Subject can only send messages and Observer can only receive messages, that is, the notification of messages is one-way. The Observer model emphasizes the unified communication to the Observer after the goal is changed, that is, the interaction between the observed and the Observer. All observers are the same

Advantages and disadvantages

  • advantage
    1. The observer and the observed are abstractly coupled
    2. A trigger mechanism has been established
  • shortcoming
    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 circular call between them, which may lead to system crash
    3. The observer model has no corresponding mechanism to let the observer know how the observed target object has changed, but only know that the observed target has changed

Applicable scenarios and application examples

  • Applicable scenario
    1. An abstract model has two aspects, one of which depends on the other. Encapsulate these aspects in separate objects so that they can be changed and reused independently
    2. The change of one object will lead to the change of one or more other objects, and it is unknown how many objects will change, which can reduce the coupling between objects
    3. An object must notify other objects without knowing who they are
    4. You need to create A trigger chain in the system. The behavior of object A will affect object B, and the behavior of object B will affect object C.. You can use the observer mode to create A chain trigger mechanism
    5. Cross system message transformation scenarios, such as the processing mechanism of message queue

  • Application examples
    1. If the mobile phone is lost, entrust someone to send a message to others
    2. Inform the teacher / boss that he is coming
    3. In an auction, the auctioneer observes the highest bid price and then notifies other bidders to bid
    4. The cat barked, which frightened the mouse and the owner. The cat is the observed, and the mouse and man are the observers
    5. In the journey to the west, Wukong asks the Bodhisattva to subdue the red boy. The Bodhisattva sprinkles water on the ground to attract an old tortoise. The tortoise is the observer. He observes the action of the Bodhisattva sprinkling water

The observer pattern decouples the subject and the concrete observer, so that both sides of the coupling rely on abstraction rather than concrete. So that their changes will not affect the changes on the other side.

reference material

1.On Design Pattern -- observer pattern
2.Observer mode
3.Observer model of design pattern
4.Design pattern album -- comparison of intermediary pattern and observer pattern


Below is my public official account of the two-dimensional code.

Tags: C++ Design Pattern

Posted on Wed, 15 Sep 2021 15:06:47 -0400 by delxy