设计模式之发布订阅模式

1,020 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

摘要

本文从上一篇的观察者模式出发,将需求进一步升级,推动代码演化到发布订阅模式。然后介绍了他的实现要点以及它和观察者模式的区别,希望对你理解这两个模式有帮助。

建议你先从观察者模式来看起。

从观察者模式出发到发布订阅者

先回顾观察者模式的代码,如下


// 大漂亮 是女神,有很多的爱慕者
const beauty = {
  lover: [], // 容器,保存全部的观察者
  notify(){ // 通知全部观察者
    this.lover.forEach(fn=>fn())
  },
  addLover(fn) { // 添加观察者
    this.lover.push(fn)
  },
  removeLover(fn) { // 移除观察者
    const idx = this.lover.findIndex(item=>item ===fn);
    if(idx !=-1) {
      this.lover.splice(idx,1)
    }
  }
}
const lover1 = () => {console.log('1号爱慕者')}
const lover2 = () => {console.log('2号爱慕者')}
const lover3 = () => {console.log('3号爱慕者')}
beauty.addLover(lover1)
beauty.addLover(lover2)
beauty.addLover(lover3)
beauty.notify()// 1,2,3号
beauty.removeLover(lover1)
beauty.notify()// 2,3号

上面代码的核心要点:

  1. 一个容器来保存观察者
  2. 一个动作通知全体观察者
  3. 两个动作(添加,移除)观察者

注意哈,上面的 目标(大漂亮)与观察者(爱慕者)之间还是耦合在一起(大漂亮亲自添加爱慕者:beauty.addLover(lover) )的。

接下来把上面的代码主角 大漂亮 升级成 ****万人迷 , 她的粉丝也多了,她实在没有精力自己去维护粉丝群了,所以她迫切需要一个第三方(经纪人)来管理她和粉丝之间的联系:

(1)不同的粉丝关注万人迷的不同动向(拍电影和拍广告); 

(2)动向要精准通知到不同的粉丝群体。

代码如下:


// 经纪人
const agent = {
  // 容器,保存全部的观察者
  center: {},
  // 添加指定事件的观察者
  addLover(eName,fn) {
    if( !this.center[eName] )
      this.center[eName] = []
    this.center[eName].push(fn)
  },
  // 移除指定事件的观察者
  removeLover(eName, fn) {
    if( !this.center[eName] ) return
    const listener = this.center[eName]
    const idx = listener.findIndex(item=>item===fn);
    if(idx !=-1) {
      listener.splice(idx,1)
    }
  },
  // 发布事件
  notify(eName) {
    if( !this.center[eName] ) return
    const listener = this.center[eName]
    listener.forEach(fn => fn())
  }
}
// 大漂亮 升级为大明星
// 有了经纪人
const beauty = {
  agent: agent,
  notify(eName) { 
    // 让她的经纪人去通知
    this.agent.notify(eName )
  }
}
const lover1 = () => {console.log('1号观众')}
const lover2 = () => {console.log('2号观众')}
const lover3 = () => {console.log('3号影迷')}
// 经纪人负责管理粉丝
agent.addLover('拍广告', lover1)
agent.addLover('拍广告', lover2)
agent.addLover('拍电影', lover3)
beauty.notify('拍广告')// 1,2号
agent.removeLover('拍广告', lover1)
beauty.notify('拍广告')// 2号

上面的代码的特点是:

(1) 代码:有一个事件管理中心, 结构类似  {事件名1:[观察者1,观察者2],事件名2:[观察者1]}  这样的键值对,属性名表示不同的事件,属性值是一个数组,用来保存观察者们。

(2) 逻辑:观察者(粉丝)和目标(大漂亮)之间多了一层结构(经纪人)。

我们来回顾一下原由:事变得复杂了,需求升级了,所以简单的观察者模式代码升级成了订阅发布者模式了。

一切进步的原由都是更高级的挑战。

发布订阅者模式

GoF在1995年提出23种设计模式中,只有观察者模式(Observer) ,而发布订阅者模式(Publisher/Subscriber) 并不在23中之列。它算是观察者模式的一种特殊扩展(也有人说是:发布订阅模式 又叫做 观察者模式)。他们两个实现结构的区别如下:

image.png 举个发布订阅者模式的例子:

你在微博上关注了大V,同时其他很多人也关注了大V。当大V发布动态的时候,微博就会为你们推送这条动态。

大V就是发布者,你是n多个订阅者之一,微博就是经纪人,你和大V是没有直接的消息往来的,全是通过微博来协调的(你的关注,大V的发布动态)。

可以看出:它的特点是目标 和观察者之间有一个独立的第三方来联系。

对比他们的区别

角色个数:

  • 观察者模式 :   有两个角色 观察者 + 目标(被观察者)
  • 发布订阅模式:有三个角色 发布者+订阅者 + 中间件(额外的事件处理中心)

耦合关系:

  • 观察者和被观察者,是松耦合的关系
  • 发布者和订阅者,则完全不存在耦合

这里应那句经典的论断:任何软件工程遇到的问题都可以通过增加一个中间层来解决! (比如:观察者模式中观察者和目标并没有解耦,而发布订阅者模式下,发布者和订阅者是解耦的。比如:在windows上安装linux虚拟机来跑linux)

复杂度:

  • 观察者模式,简单
  • 发布订阅模式,更复杂

观察者模式是不是发布订阅模式?

我没有答案, 但这里有两个基本的事实:

它们的意图相同但结构不同

它们的设计意图是一样:是对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。(一变 百变

它们的设计结构是不同的:发布订阅模式相比观察者模式多了一个中间件(事件处理中心)

所以,如果以设计意图来界定设计模式,那它们是一样的,以设计结构来界定,它们就不同。

设计模式并不是固定不变的

设计模式的家族是不断发展的。在1995年提出了23种设计模式,后面增加到了24种,也慢慢有更多的模式已经被总结出来,例如:模块模式、沙箱模式等。

小结

本文介绍了发布订阅者模式的实现代码,及它和观察者模式的区别,希望对你有帮助。