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