Is the publish/subscribe model the same as the Observer model?

In many places we can see frameworks, functions, or plug-ins based on either of these or some design pattern

In the browser, addEventListener(type, FN) is used to delegate events to DOM elements that listen for asynchronous operations by the user

Android also has a lightweight framework for publishing/subscribing to events, EventBus, which works like the Web

Many methods of socket. IO are also based on this pattern, listening and firing events, batch broadcasting, etc

There is also a Events event trigger in Node that handles synchronous responses to asynchronous operations

What is the difference between the two? The following two diagrams can briefly describe their process (publisher/subscriber mode I directly use addeventListener and DispatchEvent to describe to help understand).

The Observer Pattern is:

Subject(Subject, or observed) : The associated object in the queue needs to be notified when the status transmission changes

Observer: When the Subject sends a message, the information is retrieved through a callback

The Observer throws the event (denoted as fn callback) to the Subject, and then starts monitoring its every move. When the Subject’s (asynchronous) task is complete, the fn callback synchronously fires the event to transmit the message to the Observer, completing a full cycle

Implementation process:

The Observer. Js:

module.exports = class Observer { // Define an observer class. Each instantiated observer has a subscribe function
    constructor() {}
    /** * subscribe *@param Target Specifies the instance object * of the observed Subject@param Fn subscribes to the registered callback */
    subscribe(target, fn) {
        target.observerList.push(fn)
    }
}
Copy the code

Subject. Js:

module.exports = class Subject { // Define the observed class, each instantiated with a list of registered observer callbacks and a fireEvent function
    constructor() {
        this.observerList = []
    }
    /** ** triggers *@param E The parameter passed by the observer to the observer */
    fireEvent(e) {
        this.observerList.forEach(item= > {
            item(e)
        })
    }
}
Copy the code

Main.js (Usage Scenarios)

const Observer = require('./js/observer');
const Subject = require('./js/subject')

class MyObserver extends Observer {}
class MySubject extends Subject {}

// Instantiate two observers listening on a subject at the same time
const observer = new MyObserver()
const observer2 = new MyObserver()
const subject = new MySubject()

observer.subscribe(subject, (e) = > {
    console.log(e) //hello world
})
observer2.subscribe(subject, (e) = > {
    console.log(e) //hello world
})
// Delay the activation of the observer registration function, passing parameters
setTimeout(subject.fireEvent.bind(subject, 'hello world'), 1000)
Copy the code

Publisher-subscriber Pattern:

Subscriber: Registers events to an Event dispatch center (Event Channel or EventBus)

Publisher: Triggers an event in the dispatch center

Event Channel, similar to EventBus in Vue and Android: After receiving the message of Publisher, the Event of Subscriber registration is processed uniformly

Subscriber registers the Event to the Event Channel(scheduling center) through ON, and transfers data with the Event Channel through callback. When Subscriber(Subscriber) triggers the Event Channel(scheduling center) and transfers data to it, the scheduling center will activate the connection established with Subscriber(Subscriber) and send data through emit. After receiving the data, the Subscriber will complete a cycle

Implementation process:

eventBus.js

// Publish/subscribe design patterns (Pub/Sub)
class EventBus {
    constructor() {
        this._eventList = {} // List of dispatch centers
    }

    static Instance() { // Returns a singleton instance of the current class
        if(! EventBus._instance) {Object.defineProperty(EventBus, "_instance", {
                value: new EventBus()
            });
        }
        return EventBus._instance;
    }
    /** * Register events with the dispatch center *@param Type Indicates the event type, especially the event name *@param Fn event registered callback */
    onEvent(type, fn) { / / subscriber
        if (!this.isKeyInObj(this._eventList, type)) { // If the dispatch center does not find the queue for the event, create a new event list (multiple callback functions can be registered for a certain type of event)
            Object.defineProperty(this._eventList, type, {
                value: [].writable: true.enumerable: true.configurable: true})}this._eventList[type].push(fn)
    }
    /** * Triggers one or more functions registered under this event type in the dispatch center *@param Type Indicates the event type, especially the event name *@param The argument */ passed by the data publisher
    emitEvent(type, data) { / / publisher
        if (this.isKeyInObj(this._eventList, type)) {
            for (let i = 0; i < this._eventList[type].length; i++) {
                this._eventList[type][i] && this._eventList[type][i](data)
            }
        }
    }
    offEvent(type, fn) { // Destroy the listener
        for (let i = 0; i < this._eventList[type].length; i++) {
            if (this._eventList[type][i] && this._eventList[type][i] === fn) {
                this._eventList[type][i] = null}}}/** * Checks if the object contains this property, except the prototype chain *@param Obj The object to be checked *@param Key Indicates the property */ of the checked object
    isKeyInObj(obj, key) {
        if (Object.hasOwnProperty.call(obj, key)) {
            return true
        }
        return false}}module.exports = EventBus.Instance()
Copy the code

main.js

const EventBus = require('./js/eventBus')
let list = [], // Log asynchronous operations
    count = 0./ / counter
    timeTick = setInterval(function () {
        if (count++ > 3) { // When the execution reaches a certain time, destroy the event and timer
            EventBus.offEvent('finish', eventHandler)
            clearInterval(timeTick)
        }
        list.push(count)
        EventBus.emitEvent('finish', {
            list
        })
    }, 1000)

EventBus.onEvent('finish', eventHandler)

function eventHandler(e) {
    console.log(e)
    // { list: [ 1 ] }
    // { list: [ 1, 2 ] }
    // { list: [ 1, 2, 3 ] }
    // { list: [ 1, 2, 3, 4 ] }
}
Copy the code

Summary: The publisher/subscriber pattern is actually optimized based on the observer pattern, but there are some differences

Observer pattern: Defines a one-to-many dependency between objects in which all dependent objects are notified and automatically updated when an object’s state changes

Advantages: The observer and the observed are abstractly coupled, and the two have established a trigger mechanism, loose coupling

Disadvantages: Cyclic dependencies between the two, if the relationship is complex, such as too many observers, still cause performance problems, the solution is to avoid synchronous execution caused by thread blocking

Publisher/subscriber pattern: Similar to the observer pattern, but the core difference is that publishers and subscribers are decoupled from each other and do not know the identity of the notified and notified parties. Instead, registered functions are managed in a unified scheduling center

Advantages: Fully decoupled publisher/subscriber, high scalability, commonly used in distributed, tightly coupled services

Disadvantages: Publishers decouple subscribers, which is both a major advantage and a disadvantage. For example, in sockets, if a server sends a message to a client, it doesn’t care if the message is sent successfully. In this case, the client needs to return the message to ensure the reliability and availability of the code

Related source code:Yards cloud address