Write a publish subscribe schema with JS
- 2021-12-04 18:02:25
- OfStack
What is the publish-subscription mode? Can you realize 1 stroke by hand? Is it different from the observer model? ...
1 Scene introduction
Let's look at such a scene first:
Suppose there is now a social platform with a big V called
Nami
Nami is very talented and versatile. At present, she has two skills: she can write songs and shoot videos
She will publish these works on the platform. Fans who follow her will receive these contents
Now he has three fans: Luffy, Zoro and Sanji
Every time Nami1 publishes a work, the messages received on the accounts of the three fans will be updated
Now express it 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 = {
// As long as Nami The works of 1 Update, this method will be called
workUpdate: function () {
// Get a work
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 coming
IfNami
I gained another fan
Robin
I want to add 1
robin
Object, and you want to modify the
workUpdate
Method
If
Nami
I have a new skill: writing novels, I have to revise them
workUpdate
Function, and modify the
update
Method, because the parameter is increased by 1
Have you found any problems?
The coupling degree between fan object and large V object is too high, which makes it difficult to extend each other
2 Code Optimization
2.1 Solve the problem of adding fans
First, solve the first problem above, so that you don't need to modify it when you add fans
workUpdate
Method
First, we abstract "big V" into a class Star, and use an array
fans
To save the list of fans and add 1 method to add fans
Nami
0
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, "fan" is also abstracted into a class Fan. When we create the fan object, we pass in the "big V" object and call the big V
Nami
0
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 we don't have to change the code to add fans
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 Solve the problem of adding works
We added 1
works
Array to save the works of large V and add it
get
And
set
Method
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 adding works to the big V:
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
As you can see, in the above example, 1
nami
There is a 1-to-many dependency relationship between object and multiple fan objects, when
nami
When an object has an update, all fans who pay attention to her will be notified.
In fact, this is the observer model
Observer pattern: Defines a 1-to-many dependency relationship between objects. When the state of an object changes, all objects that depend on it are notified and updated automatically
We abstracted the code in 2.2 one step further:
Think of "fans" as observers (
Observer
), and regard "big V" as an object to be observed, which is called a theme (
Subject
)
Subject
Maintain a list of observers
observerList
(Original fans array). When
Subject
When the state of the original work changes (the original work is updated), it is called notify (the original work is updated)
workUpdate
) method notifies all observers to execute their
update
Method
The specific code is as follows:
// Observed: Subject
class Subject {
constructor() {
this.observerList = [];
// Represents topic state
this.state = 0;
}
addObserver(observer) {
this.observerList.push(observer);
}
// Change topic status
setState(state) {
this.state = state;
// Notify all observers when the 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 brokers come on stage
Due to the busy business of V, they need agents to maintain the contact between artists and fans
The broker's work includes:
Maintain fans of big V, and the broker will have a fan list in his hand The new works of V will be handed over to the broker, who will be responsible for sending the new works to fans in the fan listAbstracted into one 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 is the same as Star class 1 in 2.2, except that the class name is changed.
Does it make sense to do so?
In fact, code 1 is identical because in the Star class of 2.2 we only write about publishing (that is, publishing works) and subscribing (that is, maintaining fan lists); The Star class itself may do more than this, such as authoring content.
Now we will
Star
The work of publishing and subscribing in the class is pulled out and handed over to
Manager
Fully responsible. And
Star
Class only needs to hand over the work after the creation is completed
Manager
That's enough
On the other hand, fans
Fan
It is no longer directly related to
Star
There is an interaction, and Fan only cares about whether it can receive the work, so
Fan
Direct sum
Manager
Interaction occurs and Fan unsubscribes (this behavior is equivalent to the
Manager
Add fans to the maintained fan list)
Manager
And from
Manager
Get the works you want there
So the code for Star and Fan is as follows:
class Star {
constructor() {}
// Creative
create(manager) {
// Will be 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 and Subscribe Mode
Previously, we used brokers to take charge of publishing and subscribing, instead of letting
Star
And
Fan
Direct interaction occurs, which achieves the decoupling effect of the two
This is the publish-subscribe pattern
We abstract Manager in 4 one step further:
Think of "fans" as subscribers (
Subscriber
); Think of "Big V" as the publisher of content, which is called publisher in publish-subscription mode (
Publisher
); Think of "brokers" as publishing and subscription centers (or middlemen)
Broker
)
The specific code is as follows:
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";
}
}
0
Run 1 to see the effect:
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";
}
}
1
6 Comparison of Observer Mode and Publish-Subscribe Mode
In terms of the number of roles,
The observer pattern has only two roles: the observer and the observed The publish-subscription pattern has three roles: publisher, subscriber, and middleman (publish-subscription hub)From the perspective of coupling degree,
The observer pattern is in a loosely coupled state, that is, the two still interact, but it is easy to expand and not affect each other Publisher and subscriber in publish-subscribe mode have no coupling at all, so the effect of decoupling between objects is achievedFrom the point of view of intention
Both of them: One kind of 1-to-many dependency relationship between objects is realized. When the state of an object changes, all objects that depend on it will be notified and updated automatically