What is a publish subscription model? Can you do this by handwriting? Is it different from the observer mode?...
1 Scene introduction
Let's start with a scenario like this:
Suppose you have a social platform with a V called Nami
Nami is brilliant and versatile. Currently she has two skills: she can write songs and take videos.
She will publish these works on the platform. Fans who follow her receive this content
Now he has three fans: Luffy, Zoro, Sanji
Every time Nami publishes a work, the messages received on the accounts of three fans are updated
Now in code:
const luffy = { update: function (songs, videos) { console.log(songs, videos); }, }; const zoro = { update: function (songs, videos) { console.log(songs, videos); }, }; const sanji = { update: function (songs, videos) { console.log(songs, videos); }, }; const nami = { // This method will be called whenever Nami's work is updated workUpdate: function () { // Getting Works const songs = this.getSongs(); const videos = this.getVideos(); // Account Update luffy.update(songs, videos); zoro.update(songs, videos); sanji.update(songs, videos); }, getSongs: function () { return "mp3"; }, getVideos: function () { return "mp4"; }, };
Now the problem is
- If Nami harvested another fan, Robin, I would add a robin object and modify the workUpdate method
- If Nami has a new skill: Writing a novel, I will modify both the workUpdate function and the update method in each fan object, because the parameter adds a new one
Did you find any problems?
The coupling between fans and big V objects is too high, making it difficult for them to expand independently
2 Code optimization
2.1 Solve the problem of adding fans
Fix the first problem above so that you don't have to modify the workUpdate method when adding fans
First, we abstract Big V into a Star class, save the list of fans with array fans, and add a new way to add fans
class Star { constructor() { this.fans = []; } addFans(fan) { this.fans.push(fan) } workUpdate() { const songs = this.getSongs(); const videos = this.getVideos(); this.fans.forEach((item) => item.update(songs, videos)); } getSongs() { return "MP3"; } getVideos() { return "MP4"; } }
Then, we abstract "fans" into a Fan-like object. When we create a fan object, we pass in a "big V" object and call the big V's addFans method to add to the fan list.
class Fan { constructor(name, star) { this.name = name this.star = star this.star.addFans(this) } update(songs, videos) { console.log(songs, videos); } }
Now that we add fans, we don't have to change the code anymore
const nami = new Star() const luffy = new Fan("luffy", nami); const zoro = new Fan("zoro", nami); const sanji = new Fan("sanji", nami); const robin = new Fan("robin", nami); nami.workUpdate()
2.2 Solving the problem of adding works
We added an array of works to hold the work of Big V and add get and set methods to it
class Star { constructor() { this.fans = []; this.works = []; } addFans(fan) { this.fans.push(fan); } setWorks(work) { this.works.push(work); // After adding a work, call the update method this.workUpdate(); } getWorks() { return this.works; } workUpdate() { this.fans.forEach((item) => item.update()); } }
Modify the class Fan accordingly:
class Fan { constructor(name, star) { this.name = name this.star = star this.star.addFans(this) } update() { console.log(`${this.name}:${this.star.getWorks()}`) } }
Now you don't have to change the code when Big V adds your work:
const nami = new Star(); nami.setWorks('song') nami.setWorks('video') nami.setWorks('novel') const luffy = new Fan("luffy", nami); const zoro = new Fan("zoro", nami); const sanji = new Fan("sanji", nami); nami.workUpdate();
3 Observer mode
You can see that in the above example, there is a one-to-many dependency between a nami object and multiple fan objects, and when a nami object has a work update, all those who care about her will be notified.
In fact, this is the observer model
Observer mode: Defines a one-to-many dependency between objects, in which all dependent objects are notified and automatically updated when the state of an object changes
We will further abstract the code in 2.2:
View "fans" as observers and "big V" as objects to be observed, called Subjects
Subject maintains an observer list (the original fans array). When the status of a Subject changes (the original work is updated), notify all observers by calling the notify (original workUpdate) method and execute their update method
The code is as follows:
// Observed: Subject class Subject { constructor() { this.observerList = []; // Represents the subject state this.state = 0; } addObserver(observer) { this.observerList.push(observer); } // Change Theme Status setState(state) { this.state = state; // Notify all observers when state changes this.notify(); } getState() { return this.state; } notify() { this.observerList.forEach((observer) => observer.update()); } } // Observer class Observer { constructor(name, subject) { this.name = name; this.subject = subject; this.subject.addObserver(this); } update() { console.log(`${this.name}:${this.subject.state}`); } }
4 Broker on stage
Because Big V is busy, they need a broker to keep the artists connected with their fans
The broker's work includes:
- Maintain big V fans, broker will have a list of fans
- Big V's new work is handed over to the broker, who is responsible for sending the new work to the fans on the fan list
Abstract into a class as follows:
class Manager { constructor() { this.fans = []; this.works = []; } addFans(fan) { this.fans.push(fan); } setWorks(work) { this.works.push(work); // After adding a work, call the update method this.workUpdate(); } getWorks() { return this.works; } workUpdate() { this.fans.forEach((item) => item.update()); } }
Hmm? Where does this code seem to have been seen?
Yes, it's the same as the Star class in 2.2, just changing the class name.
Does that make sense?
In fact, the code is the same because in the Star class of 2.2 we only wrote about publishing (i.e., publishing works) and subscribing (i.e., maintaining fan lists); The Star class itself may do more than that, for example, authoring content.
Now we take the publishing and subscription work out of the Star class and leave Manager solely responsible. The Star class simply gives the work to Manager after the creation is complete
Fan, on the other hand, no longer interacts directly with Star. Fan only cares about receiving the work, so Fan interacts directly with Manger, Fan subscribes (which acts like adding fans to the list of fans maintained by Manger) Manager and gets the work he wants from Manager
So the code for Star and Fan is as follows:
class Star { constructor() {} // A literary creation create(manager) { // Deliver the created new work to the broker manager.setWorks("new work"); } } class Fan { constructor(name, manager) { this.name = name; this.manager = manager; this.manager.addFans(this); } update() { console.log(`${this.name}:${this.manager.getWorks()}`); } }
5 Publish subscription mode
Previously, we used a broker to take care of publishing and subscribing without having Star and Fan interact directly to achieve a decoupling effect
This is the publish-subscribe mode
We will further Abstract Manger in 4:
Think of "fans" as Subscribers; Think of Big V as the publisher of content, which is called Publisher in publish subscription mode. Think of a broker as a publishing and subscription center (or Broker)
The code is as follows:
// Publish Subscription Dispatch Center class Broker { constructor() { this.subscribers = []; // Represents the subject state this.state = 0; } // Subscribe subscribe(subscriber) { this.subscribers.push(subscriber); } // Change Theme Status setState(state) { this.state = state; // Publish after status changes this.publish(); } getState() { return this.state; } // Release publish() { this.subscribers.forEach((subscriber) => subscriber.update()); } } // Publisher class Publisher { constructor() {} changeState(broker, state) { broker.setState(state); } } class Subscriber { constructor(name, broker) { this.name = name; this.broker = broker; this.broker.subscribe(this); } update() { console.log(`${this.name}:${this.broker.getState()}`); } }
Let's run it and see what happens:
// Create Dispatch Center const broker = new Broker() // Create Publisher const publisher = new Publisher() // Create Subscriber const subscribe1 = new Subscriber('s1', broker) const subscribe2 = new Subscriber('s2', broker) const subscribe3 = new Subscriber('s3', broker) // The publisher changes state and notifies the dispatch center, which notifies subscribers publisher.changeState(broker, 1)
6 Comparing the Observer Mode with the Publish Subscription Mode
From the number of roles
- The observer model has only two roles: the observer and the observer
- The Publish Subscription Model has three roles: Publisher, Subscriber, and Intermediate (Publish Subscription Center)
Coupling
- The observer mode is in a loosely coupled state, where the two are still interacting but can easily expand independently and not influence each other.
- Publishers and subscribers in publish-subscribe mode do not have coupling at all, which achieves decoupling effect between objects
From an intentional point of view
- Both: implements a one-to-many dependency between objects, and when the state of an object changes, all objects that depend on it are notified and automatically updated
Public Number [Front End]