introduce

There are usually two models in the observer model, one observer (observer) and an observed (Observed). Literally, when the observed has some behavior or change, the observer will be notified, and the observer will act according to the behavior or change. So how do we do thatJavaScriptThe code implements an observer pattern as shown below at 👇.

implementation

Observer pattern JS implementation

let observer_ids=0;
let observed_ids=0;
// Observer class
class Observer {
   constructor() {
      this.id = observer_ids++;
   }
   // Handle the observed changes
   update(ob){
      console.log("Observer" + this.id + '- Observed detected${ob.id}Change `); }}// The observed column
class Observed {
   constructor() {
      this.observers = [];
      this.id=observed_ids++;
   }
   // Add an observer
   addObserver(observer) {
      this.observers.push(observer);
   }
   // Delete the observer
   removeObserver(observer) {
      this.observers = this.observers.filter(o= > {
         returno.id ! = observer.id; }); }// Notify all observers
   notify() {
      this.observers.forEach(observer= > {
         observer.update(this); }); }}let mObserved=new Observed();
let mObserver1=new Observer();
let mObserver2=new Observer();

mObserved.addObserver(mObserver1);
mObserved.addObserver(mObserver2);

mObserved.notify();

Copy the code

The output is

Observer 0- Observed 0 change detected

Observer 1- Observed 0 change detected

Let’s change the execution code, add the code to delete the observer,

let mObserved=new Observed();
let mObserver1=new Observer();
let mObserver2=new Observer();
let mObserver3=new Observer();


mObserved.addObserver(mObserver1);
mObserved.addObserver(mObserver2);
mObserved.addObserver(mObserver3);
mObserved.removeObserver(mObserver2);

mObserved.notify();
Copy the code

The output is

Observer 0- Observed 0 change detected

Observer 2- Observed 0 change detected

extension

Instead of using the above two classes directly, we can use the observer and observed classes as base classes for other classes to implement.

class Teacher extends Observer{ constructor(name){ super(); this.name=name; } update(st){ // super.update(st); Console. log(St. name+ 'submitted ${this.name} job'); } } class Student extends Observed{ constructor(name){ super(); this.name=name; } submitHomeWork(){this.notify(this)}} let teacher1=new Teacher(" math "); Let teacher2=new Teacher(" language "); Let stu1=new Student("小 ling "); Let stu2=new Student("小 小 "); Let stu3=new Student("小李"); stu1.addObserver(teacher1); stu1.addObserver(teacher2); stu2.addObserver(teacher1); stu2.addObserver(teacher2); stu3.addObserver(teacher1); stu3.addObserver(teacher2); stu1.submitHomeWork(); stu2.submitHomeWork(); stu3.submitHomeWork();Copy the code

The output of the above code is

Xiaoling has submitted her Math homework Xiaoling has submitted her Chinese homework Xiaoming has submitted her Chinese homework Xiaoming has submitted her Math homework Xiaoli has submitted her Chinese homework

Publish the subscriber model

The observer pattern is also commonly referred to as the publish/subscribe pattern, in which the observer is the publisher and the observer is called the subscriber. This is also easy to understand, let’s take wechat public account subscription as an example. I can subscribe to many wechat public accounts, at which time I am the subscriber, while the wechat public account is the publisher. When a wechat public account releases a new article, the wechat public account platform will notify me, and I can read the new article after receiving the notice.

Note: The above description of the publishing subscriber pattern is incorrect, it is corrected below.Copy the code

The published subscriber mode is similar to the observer mode, but they are not identical. Compared with the observer mode, the published subscriber mode has an additional scheduling center of the middle layer, which is used to process the information published by the publisher and then publish it to the subscribers. The general process is shown in the figure below.

So why add a mid-tier scheduling center? Through the implementation of the observer pattern above 👆, ourobservedClass is holdingobserverObject, and therefore does not achieve complete decoupling of the two classes. By adding the scheduling center class in the middle tier, we can completely decouple the subscribers and publishers, so that they are no longer directly related, but are related through the scheduling center. Let’s go ahead and implement a published subscriber pattern.

/ / publisher
class Pub{
   constructor(dispatcher){
       this.dispatcher=dispatcher;
       this.id=observed_ids++;
   }
   / * * *@description: Release method *@param {type} Notification type */
   publish(type){
      this.dispatcher.publish(type,this)}}/ / subscriber
class Subscriber{
    constructor(dispatcher){
      this.dispatcher=dispatcher;
      this.id=observer_ids++;
    }
    subscribe(type){
       this.dispatcher.subscribe(type,this);
    }
    doUpdate(type,arg){
        console.log("Received the message."+arg)
    }
}
// The dispatch center
class Dispatcher{
   constructor(){
      this.dispatcher={};
   }
   / / subscribe
   subscribe(pub,subscriber){
      if(!this.dispatcher[pub.id]){
         this.dispatcher[pub.id]=[];
      }  
      this.dispatcher[pub.id].push(subscriber);
   }
   / / unsubscribe
   unsubscribe(pub, subscriber) {
      let subscribers = this.dispatcher[type];
      if(! subscribers || ! subscribers.length)return;
      this.dispatcher[type] = subscribers.filter(item= >{ 
         returnitem.id ! == subscriber.id }); }/ / release
  publish(type, args) {
      let subscribers = this.dispatcher[type];
      if(! subscribers || ! subscribers.length)return;
      subscribers.forEach(subscriber= >{ subscriber.doUpdate(type,args); }); }}class Reader extends Subscriber{
   constructor(name,dispatcher){
      super(dispatcher);
      this.name=name;
   }
    doUpdate(type,st){
      // super.update(st);
        console.log(this.name+'Read --${type}-- The article of the public account); }}class WeiX extends Pub{
    constructor(name,dispatcher){
       super(dispatcher);
       this.name=name;
    }
    publishArticle(type){
       this.publish(type)
    }
}

let dispatcher=new Dispatcher();
/ / the public number
let wei1=new WeiX("Front end",dispatcher);
let wei2=new WeiX("Database",dispatcher);
/ / readers
let reader1=new Reader("Xiao ling",dispatcher);
let reader2=new Reader("Xiao Ming",dispatcher);
let reader3=new Reader("Xiao li",dispatcher);
// Readers subscribe to the public account
reader1.subscribe("Front end");
reader2.subscribe("Database");
reader3.subscribe("Database");
// The official account publishes articles
wei1.publishArticle("Front end");
wei1.publishArticle("Database");
Copy the code

The running results are as follows:

Xiaoling read — front end — the article of public number Xiaoming read — database — the article of public number Xiaoli read — database — the article of public number

We did implement a simple publish-subscribe model with the above code, but we found that the Pub class was not necessary because it simply called the publish method of the Dispatcher. So we can simplify the above code.

let ids=0;
let observer_ids=0;
/ / subscriber
class Subscriber{
    constructor(dispatcher){
      this.dispatcher=dispatcher;
      this.id=observer_ids++;
    }
    subscribe(type){
       this.dispatcher.subscribe(type,this);
    }
    doUpdate(type,arg){
        console.log("Received the message."+arg)
    }
}
// The dispatch center
class Dispatcher{
   constructor(){
      this.dispatcher={};
      this.id=ids++;
   }
   / / subscribe
   subscribe(type,subscriber){
      if(!this.dispatcher[type]){
         this.dispatcher[type]=[];
      }  
      this.dispatcher[type].push(subscriber);
   }
   / / unsubscribe
   unsubscribe(type, subscriber) {
      let subscribers = this.dispatcher[type];
      if(! subscribers || ! subscribers.length)return;
      this.dispatcher[type] = subscribers.filter(item= >{ 
         returnitem.id ! == subscriber.id }); }/ / release
  publish(type, args) {
      let subscribers = this.dispatcher[type];
      if(! subscribers || ! subscribers.length)return;
      subscribers.forEach(subscriber= >{ subscriber.doUpdate(type,args); }); }}class Reader extends Subscriber{
   constructor(name,dispatcher){
      super(dispatcher);
      this.name=name;
   }
    doUpdate(type,st){
      // super.update(st);
        console.log(this.name+'Read --${type}-- The article of the public account); }}class WeiX extends Dispatcher{
    constructor(name){
       super(a);this.name=name;
    }
    publishArticle(type){
       this.publish(type)
    }
}
// Wechat public account platform
let wx1=new WeiX();
/ / readers
let reader1=new Reader("Xiao ling",wx1);
let reader2=new Reader("Xiao Ming",wx1);
let reader3=new Reader("Xiao li",wx1);
// Readers subscribe to the public account
reader1.subscribe("Front end");
reader2.subscribe("Database");
reader3.subscribe("Database");
// The official account publishes articles
wx1.publishArticle("Front end");
wx1.publishArticle("Database");
Copy the code

The last

The above is my understanding of the observer mode after reading the relevant chapters of “Zen of Design Patterns”. If there are any improper points, please correct them! Thanks fengwei for correcting my misdescriptions of observer mode and publication subscriber mode in this article!