0 x0, introduction

To liver “Python crawler from entry to prison” learning notes | Python topic month + some chores, haven’t chew the beauty of the design pattern for a long time, to learn a little before forget, ha ha, continue to learn downward today, in this paper, the corresponding design patterns and paradigm: Behavior Pattern (56-57), Observer Pattern. Creation type → object creation problem, structure type → combination or encapsulation of classes and objects, behavior type → interaction between classes or objects.

Tips: Second-hand knowledge processing is hard to avoid mistakes, interested in time can be consulted by themselves, thank you.


0 x1, definition

Also known as the subscription-publish pattern, a one-to-many dependency is defined between objects, and all dependent objects are automatically notified when the state of one object changes. The dependent object is called the observed, and the dependent object is called the observer.

It sounds a bit abstract, but here’s a simple example to help you understand (pollination by bees or butterflies) :

// Observer: insect interface
public interface Insect {
    void startWork(a);
    void stopWork(a);
}

// Specific observer: bees
public class Bee implements Insect {
    private String name;
    public Bee(String name) { this.name = name; }
    @Override public void startWork(a) { System.out.println("Bees." + name + "】 to initiate pollination."); }
    @Override public void stopWork(a) { System.out.println("Bees." + name + ") cessation of pollination"); }}// Specific observer: bees
public class Butterfly implements Insect {
    private String name;
    public Butterfly(String name) { this.name = name; }
    @Override public void startWork(a) { System.out.println("Butterfly [" + name + "】 to initiate pollination."); }
    @Override public void stopWork(a) { System.out.println("Butterfly [" + name + ") cessation of pollination"); }}// Observed: plant interface
public interface Plant {
    void registerInsect(Insect insect);
    void unregisterInsect(Insect insect);
    void notifyInsect(boolean isOpen);
}

// Specific observed: flowers
public class Flower implements Plant {
    private final List<Insect> insects = new ArrayList<>();

    @Override public void registerInsect(Insect insect) { insects.add(insect); }

    @Override public void unregisterInsect(Insect insect) { insects.remove(insect); }

    @Override public void notifyInsect(boolean isOpen) {
        if(isOpen) {
            System.out.println("Flowers bloom!");
            for(Insect insect: insects) insect.startWork();
        } else {
            System.out.println("The flowers are fading.");
            for(Insect insect: insects) insect.stopWork(); }}// Define a batch unbinding method
    public void unregisterAllInsect(a) {
        for(int i = 0; i < insects.size(); i++) unregisterInsect(insects.get(i)); }}// Test case
public class ObserverTest {
    public static void main(String[] args) {
        Flower flower = new Flower();
        // Create and register observers
        for (int i = 1; i < 4; i++) {
            flower.registerInsect(new Bee(i + ""));
            flower.registerInsect(new Butterfly(i + ""));
        }
        // Notify the observer
        flower.notifyInsect(true);
        System.out.println("=== Flowering time is over ===");
        // Notify the observer
        flower.notifyInsect(false);
        // Unbind all observersflower.unregisterAllInsect(); }}Copy the code

The output of the code is as follows:

The code is very simple, and the old rules bring out UML class diagrams, role interpretation, application scenarios, and pros and cons:

Role Interpretation:

  • Subject → also known as publisher, Subject, target, subscriber, etc., usually refers to the collection of related objects concerned by the observer;
  • ConcreteSubject → Concrete implementation class that implements methods defined by the observed, with a container for the observer;
  • Observer → also called subscriber, provides the method of responding to the changes of the observed;
  • ConcreteObserver → Concrete implementation of the observer;

Application scenario:

  • When the state of an object changes and other objects need to be modified;
  • You just want to send notifications when an object changes, without knowing who the recipient is;
  • Chain trigger mechanism: build A trigger chain in the system, A affects B, B affects C;
  • Create event-based scenarios;

advantages

Abstract decoupling between observer and target, improved scalability, dynamic linkage (one operation leads to other related operations)

Disadvantages:

When there is mutual dependence between the observer and the observed, the infinite loop caused by mutual notification should be avoided! Making the code more difficult to understand, the number of observer objects, the amount of time it takes for the observer to inform the observer becomes, to some extent, a positive impact on the efficiency of the program.


0x2 Observer mode push and pull

Push the way

Observer → Observer pushes details of the topic (usually all or part of the data of the observer), whether the observer needs it or not.

Pull a way

From the observer to the observer, only a small amount of information is passed. If the observer needs more detailed information, he/she can proactively get it from the observer. The general way to achieve this is that the observer passes the information to the observer through the update() method, and the observer obtains the information as needed through the instance.

In push mode, it is assumed that the observer knows the data required by the observer, while in pull mode, the observer does not know the specific data required by the observer and simply passes itself to the observer in case there is no other way.


0x3 Observer mode support in Java

Java’s java.util package provides an Observable class and an Observer interface to make it easier to implement the Observer pattern.

Core usage: The observed implementation inherits from the Observable. The Observer implements the Observer interface, notifies the change, and calls the setChange method

The following is a simple code example:

// The observed
import java.util.Observable;
import java.util.Observer;

public class CodingBoy extends Observable {
    private String title;
    private String contentUrl;
    
    public String getTitle(a) { return title; }
    public String getContentUrl(a) { return contentUrl; }
    
    public void update(String title, String url) {
        this.title = title;
        this.contentUrl = url;
        System.out.println("The Official account of The Boy Who Butts Off has been updated with the following:" + title);
        this.setChanged();  // Necessary, notify change
        this.notifyObservers(this); // Here is the pull mode}}/ / observer
public class Fan implements Observer {
    private String name;

    public Fan(String name) { this.name = name; }

    @Override
    public void update(Observable o, Object arg) {
        // Pull mode, through the instance to obtain the required information on demand
        CodingBoy codingBoy = (CodingBoy) arg;
        System.out.println("Fan [" + name + "] Received public post update push [" + codingBoy.getTitle() + "] (" + codingBoy.getContentUrl() + ")"); }}// Test case
public class ClientTest {
    public static void main(String[] args) {
        CodingBoy codingBoy = new CodingBoy();
        // Registered observer
        for (int i = 1; i < 4; i++) codingBoy.addObserver(new Fan(i + ""));
        codingBoy.update("Notes on Python Crawlers from Introduction to Prison."."https://juejin.cn/post/6985093530473463816");
        // Unregister the observercodingBoy.deleteObservers(); }}Copy the code

The output of the code is as follows:

Very simple.


0x4, Meal: Pattern application instance → EventBus source interpretation

The observer pattern can be implemented in different ways according to different scenarios and requirements. All of our implementations are in-process synchronous congestion. In some scenarios requiring quick response (such as registration), we need to change to asynchronous non-congestion, and in cross-process scenarios, we need to change to other implementations, such as MQ.

Here to analyze the Android EventBus transaction bus source (version :3.1.1), understand the asynchronous non-blocking implementation of the specific gameplay ~

The use of EventBus is simple:

// Register subscriber
EventBus.getDefault().register(this);

// To write an event subscription method, you must add @subscribe annotation!!
@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100)
public void onMessageEvent(MessageEvent event) {}// Send the event
EventBus.getDefault().post(new MessageEvent("Hello EventBus!"));  
EventBus.getDefault().postSticky(new MessageEvent("Hello EventBus!"));    // Sticky events

// Sticky events deal with issues where the publisher sends the event first, but the subscriber has not yet been generated,
// After a certain period of time, the subscriber will subscribe to the event, that is, after sending the event, the subscriber will receive the event.

// Cancel registration
EventBus.getDefault().unregister(this);
Copy the code

(1) the initialization

Start with eventbus.getDefault () and follow:

Thread safety belt lazily loaded DCL singleton, instance does not exist, call constructor to initialize some configuration of Eventbus,

â‘¡ Subscriber subscribing

GetDefault () is to get an EventBus singleton, go down to register(class instance) :

SubscriberMethodFinder. FindSubscriberMethods SubscriberMethod () returns a * * * * list, with the class:

Once you’ve learned how to subscribe, go down and see how to find findSubscriberMethods() :

Tips: EventBus 3.0 provides EventBusAnnotationProcessor annotation processor at compile time by reading @ the Subscribe () annotations and parse, processing of the information contained in, and then generate Java classes to save all the subscriber information about subscription, This is faster than dynamically fetching it at run time using reflection, so ignoreGeneratedIndex defaults to false~

FindUsingInfo () retrieves the information in the MyEventBusIndex class, traverses the method data that generates subscriptions in the subscriber, and findUsingReflection() :

With the prepareFindState () :

Static arrays save FindState to avoid duplicate creation:

The above loop recursively subscribing classes and their parent classes (up to the parent classes Java, Javax, Android. At the beginning so far), recursive calls at the same time findUsingReflectionInSingleClass () method:

FindSubscriberMethods () gets all the subscribing method data in the subscribing class by reading the compile-time generated file or reflection, and then take a look at what the subscribe() subscribing method does:

So the core logic of this step is:

Get information of all subscribers who subscribe to this event type → insert subscriber information into subscriber queue according to priority → get all event queues of current subscriber and add current event to facilitate subsequent unsubscription → Post this event directly to current subscriber if it is a sticky event.


â‘¢ Subscriber unsubscribing

Follow the unregister() method directly:

UnsubscribeByEventType () makes it easy to unsubscribe


â‘£ Publish common events

Follow the post() method:

The currentPostingThreadState here is a ThreadLocal < PostingThreadState >, thread internal storage class, can be specified thread to access data, with the postSingleEvent () :

Consider event inheritance, the last is go postSingleEventForEventType (), with:

With the postToSubscription () :

This is how EventBus executes the callback for ordinary event distribution, according to the thread pattern subscribed to the event.

(5) Release Sticky events

This is one more step to put the event into the sticky set than the normal event. The sticky event distribution has been mentioned in subscribe() above:

This is the main principle of EventBus. It is not a standard observer implementation, but it is a publish/subscribe framework with the benefits of the Observer model, such as decoupling of publishers and subscribers.


References:

  • EventBus 3.0 source analysis