谈谈观察者模式和发布订阅模式

12,424 阅读3分钟

在网上看到许多关于观察者模式和发布订阅模式的博文,发现很多人都认为观察者模式即发布订阅模式,经过进一步的学习和理解,我认为观察者模式和发布订阅模式还是有一些区别的,下面谈谈我对观察者模式和发布订阅模式的理解「PS:欢迎各路大神指正」。

观察者模式(Watcher)

观察者模式指的是一个对象(Subject)维持一系列依赖对象(Watcher),当有关状态发生变更时 Subject 对象则通知一系列 Watcher 对象进行更新。

在观察者模式中,Subject 对象拥有添加、删除和通知方法等等,而 Watcher 对象拥有更新方法等等。

在 Subject 对象添加了一系列 Watcher 对象之后,Subject 对象则维持着这一系列 Watcher 对象,当有关状态发生变更时 Subject 对象则会通知这一系列 Watcher 对象进行更新。

// Subject 对象
function Subject(){
  this.watchers = [];
}
Subject.prototype = {
  add(watcher){  // 添加
    this.watchers.push(watcher);
  },
  notify(){  // 通知
    var watchers = this.watchers;
    for(var i = 0;i < watchers.length;i++){
      watchers[i].update();
    }
  },
  remove(watcher){  // 删除
    var watchers = this.watchers;
    for(var i = 0;i < watchers.length;i++){
      if(watchers[i] === watcher){
        watchers.splice(i,1);
      }
    }
  },
}

// Watcher 对象
function Watcher(name){
  this.name = name;
}
Watcher.prototype = {
  update(){  // 更新
    console.log('my name is '+this.name);
  }
}

var sub = new Subject();
var w1 = new Watcher('ttsy1');
var w2 = new Watcher('ttsy2');
sub.add(w1);
sub.add(w2);
sub.notify();  //my name is ttsy1、my name is ttsy2

上述代码中,我们创建了 Subject 对象和两个 Watcher 对象,当有关状态发生变更时则通过 Subject 对象的 notify 方法通知这两个 Watcher 对象,这两个 Watcher 对象通过 update 方法进行更新。

在 Subject 对象添加了一系列 Watcher 对象之后,还可以通过 remove 方法移除某个 Watcher 对象对它的依赖。

var sub = new Subject();
var w1 = new Watcher('ttsy1');
var w2 = new Watcher('ttsy2');
sub.add(w1);
sub.add(w2);
sub.remove(w2);
sub.notify();  //my name is ttsy1

发布订阅模式(Publish && Subscribe)

发布订阅模式指的是基于一个主题通过自定义事件订阅,然后再通过该主题进行通知来执行自定义事件。

const pubSub = {
  list:{},
  subscribe(key,fn){  // 订阅
    if (!this.list[key]) {
      this.list[key] = [];
    }
    this.list[key].push(fn);
  },
  publish(){  // 发布
    const arg = arguments;
    const key = Array.prototype.shift.call(arg);
    const fns = this.list[key];

    if(!fns || fns.length<=0) return false;

    for(var i=0,len=fns.length;i<len;i++){
      fns[i].apply(this, arg);
    }
  },
  unSubscribe(key) {  // 取消订阅
    delete this.list[key];
  }
};

// 进行订阅
pubSub.subscribe('name', (name) => {
  console.log('your name is ' + name);
});
pubSub.subscribe('sex', (sex) => {
  console.log('your sex is ' + sex);
});
// 进行发布
pubSub.publish('name', 'ttsy1');  // your name is ttsy1
pubSub.publish('sex', 'male');  // your sex is male

上述代码的订阅是基于 name 和 sex 主题来自定义事件,发布是通过 name 和 sex 主题并传入自定义事件的参数,最终触发了特定主题的自定义事件。

可以通过 unSubscribe 方法取消特定主题的订阅。

pubSub.subscribe('name', (name) => {
  console.log('your name is ' + name);
});
pubSub.subscribe('sex', (sex) => {
  console.log('your sex is ' + sex);
});
pubSub.unSubscribe('name');
pubSub.publish('name', 'ttsy1');  // 这个主题被取消订阅了
pubSub.publish('sex', 'male');  // your sex is male

观察者模式 VS 发布订阅模式

观察者模式与发布订阅模式都是定义了一个一对多的依赖关系,当有关状态发生变更时则执行相应的更新。

不同的是,在观察者模式中依赖于 Subject 对象的一系列 Watcher 对象在被通知之后只能执行同一个特定的更新方法,而在发布订阅模式中则可以基于不同的主题去执行不同的自定义事件。相对而言,发布订阅模式比观察者模式要更加灵活多变。

我认为,观察者模式和发布订阅模式本质上的思想是一样的,而发布订阅模式可以被看作是观察者模式的一个进阶版。

设计模式只是一种思想,某一种设计模式都可以有很多种不同的实现方式,各种实现都有其优劣之分,具体的实现方式需要基于不同的业务场景。上述是我对观察者模式和发布订阅模式学习之后的一些理解,望指正。