Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

What is the observer model

Many designs often involve multiple objects interested in data changes in a particular object, and they all want to track data changes in that particular object. That is, the observer pattern can be used when there is a one-to-many relationship between objects. When an object is modified, its dependent objects are automatically notified.

The observer pattern is a mature pattern for multiple objects that want to know how the data in one object is changing. The observer mode has one object called “topic” and several objects called “observers”. There is a one-to-many dependency between “topic” and “observers”. When the state of “topic” changes, all “observers” are notified.

Main solution: one object state changes to notify other objects of the problem, but also to take into account the ease of use and low coupling, to ensure a high degree of collaboration.

The structure of the observer pattern

There are four roles in the structure of the Observer mode:

  • Subject: A topic is an interface that specifies the methods that need to be implemented for a specific topic, such as adding, deleting observers, and notifying observers of data updates.

  • Observer: An Observer is an interface that specifies the methods that a particular Observer uses to update data.

  • ConcreteSubject: A concrete topic is an instance of an interface class that implements a topic and contains data that can change frequently. A specific topic uses a collection, such as an ArrayList, that holds references to observers so that specific observers are notified of data changes.

  • ConcreteObserver: ConcreteObserver is an instance of the ConcreteObserver interface class that implements the ConcreteObserver. Contains specific observer interface the theme of the reference variable can hold specific themes, so that the observer to the specific topic add their own references to the specific topic in the collection, make oneself become the observer, or let this specific topic will be deleted from the specific theme collection, make oneself no longer is the observer.

Usage scenarios for the observer mode

1. One object needs to notify other objects when data is updated, but the object does not want to be tightly coupled to the notified objects.

2. When the data of one object is updated, the object needs to ask other objects to update their data, but the object does not know how many objects need to update their data.

The observer mode is very common in the application of real projects. For example, when you go to an ATM to withdraw money, the ATM will swallow the card if you enter the wrong password for many times. What events will be triggered when the card is swallowed? First, the camera continuously shoots fast. Second, notify the monitoring system of the occurrence of card swallowing. Third, initialize the ATM screen and return it to its original state. You can’t disable the entire ATM just because you swallowed a card. The first two actions are usually done in observer mode. An observer can broadcast messages. A message can trigger multiple events, which is a very important feature of the observer mode.

Using the observer pattern also has two key issues to address:

  • Broadcast chain problems

    If you have done database triggers, you know that there is A trigger chain problem. For example, A trigger is written on table A to update A column of data in table B, and there is A trigger on table B to update A column of data in table C, and table C also has A trigger… This database is basically destroyed! Our observer pattern is the same problem, an observer can have double identity, even if the observer, is also observed, there will be a problem, but once the chain is established, and this logic is complex, maintainability is very poor, according to the experience suggests that in an observer mode up to an object is both observer and the observed, That is, the message can be forwarded at most once (delivered twice), which is relatively manageable;

  • Processing problems asynchronously

    What if there are a lot of observers and it takes a long time to process something? So let’s do asynchronous, because asynchronous processing is about thread safety and queues, and you’ll see that a little bit more if you have time to look at Message Queue.

Implementation of the observer pattern

Observer class – Abstracts observers, defines an interface for all concrete observers, and updates itself when notified of a topic.

This interface is called the update interface, and the abstract observer is usually implemented using either an abstract class or an interface. The Update interface usually includes an Update method, called the Update method.

abstract class Observer
{
    public abstract void Update();
}
Copy the code

Subject class – Subject or abstract notifier, typically implemented as an abstract class or interface.

It stores all references to observer objects in an aggregate, and each topic can have any number of observers. Abstract topics provide an interface to add and remove observers.

public abstract class Subject {
    private List<Observer> observers = new List<Observer>();
    
    // Add an observer
    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    // Remove the observer
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    
    / / notice
    public void notify(a) {
        for(Observer item : observers) { item.update(); }}}Copy the code

The ConcreteSubject class – a concrete topic or concrete notifier that stores the state in a concrete observer object; Notifications are sent to all registered observers when the internal state of a specific topic changes.

A concrete topic role is usually implemented with a concrete class.

public class ConcreteSubject extends Subject {
    private String subjectState;
    
    // get/set method
    // ...
}
Copy the code

The ConcreteObserver class implements the update interface required by the abstract observer role to harmonize its state with the state of the topic.

A specific observer role can hold a reference to a specific subject object. The concrete observer role is usually implemented with a concrete class.

public class ConcreteObserver extends Observer {
    private String name;
    private String observerState;
    private ConcreteSubject subject;

    public ConcreteObserver(ConcreteSubject subject, String name) {
        this.subject = subject;
        this.name = name;
    }
    
    @Override
    public void update(a) {
        observerState = subject.subjectState;
        System.out.println("Observer" + name + The new state of "is" + observerState)
    }
    
    // get/set method
}
Copy the code

Client code

public static void main(String[] args){
    ConcreteSubject cs = new ConcreteSubject();

    cs.attach(new ConcreteObserver(cs, "X"));
    cs.attach(new ConcreteObserver(cs, "Y"));
    cs.attach(new ConcreteObserver(cs, "Z"));

    cs.subjectState = "ABC";
    cs.notify();
}
Copy the code

The results of

The new state of observer X is ABC and the new state of observer Y is ABC and the new state of observer Z is ABCCopy the code

Advantages and disadvantages of the observer model

Advantages:

  • The specific subject and the specific observer are loosely coupled. Because the topic interface depends only on the observer interface, a specific topic only knows that its observer is an instance of a class that implements the observer interface, but does not need to know which class. Again, because the observer only depends on the topic interface, the specific observer only knows that the topic it depends on is an instance of a class that implements the topic interface, but does not need to know which class.

  • The observer mode satisfies the “open – close principle”. The topic interface depends only on the observer interface, so that the class that creates the specific topic also depends only on the observer interface, so that if you add a new class that implements the observer interface, you don’t have to change the code of the class that creates the specific topic. Similarly, the class that creates a concrete observer depends only on the topic interface, and if you add a new class that implements the topic interface, you don’t have to modify the code that creates the concrete observer class.

Disadvantages:

  • If an observed object has many direct and indirect observers, it can take a long time to notify all of them.

  • If there is a cyclic dependency between the observer and the observing target, the observing target will trigger a cyclic call between them, possibly causing the system to crash.

  • The observer mode has no corresponding mechanism to let the observer know how the observed object has changed, but only that the observed object has changed.

conclusion

When implementing the observer mode, it should be noted that the interaction between the observer and the observed object cannot be reflected as direct calls between classes. Otherwise, the close coupling between the observer and the observed object will fundamentally violate the principle of object-oriented design. Either the observer “observing” the observed object or the observed “notifying” the observer of their changes should not be called directly.

In addition, Pub/Sub in Redis can also implement observer mode.