Write a publish subscribe schema with JS

  • 2021-12-04 18:02:25
  • OfStack

Directory 1 Scenario Introduction 2 Code Optimization 2.1 Solve the problem of adding fans 2.2 Solve the problem of adding works 3 Observer Mode 4 Broker Debut 5 Publish Subscribe Mode 6 Comparison between Observer Mode and Publish Subscribe Mode

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

If Nami 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 Nami0


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 Nami0 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 list

Abstracted 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 achieved

From 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

Related articles: