发布订阅模式和观察者模式真的不一样

37次阅读

共计 3878 个字符,预计需要花费 10 分钟才能阅读完成。

背景

设计模式的定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。

设计模式并不能直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案,它不是一个死的机制,它是一种思想,一种代码的形式。

每种语言对于各种设计模式都要它们自己的实现方式,对于某些设计模式来说,可能在某些语言下并不适用,比如工厂模式就不适用于JavaSctipt。模式应该用在正确的地方,而所谓正确的地方只有我们深刻理解模式的意图后,再结合项目的实际场景才知道。

观察者模式 (Observer Pattern)

观察者模式定义了 对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知,并自动更新。

观察者模式属于 行为模式 ,行为模式 关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。

观察者模式还有一个别名叫“发布 - 订阅模式”,又或者“订阅 - 发布模式”,订阅者和订阅目标是联系在一起的,当订阅目标发生改变时,逐各通知订阅者。
我们用报纸期刊的订阅来举例说明,当你订阅一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸,报社和订报纸的客户就是定义里描述的“一对多”的依赖关系。

发布 - 订阅模式 (Pub-Sub Pattern)

其实 24 种基本设计模式中,并没有发布 - 订阅模式,上面也解释了,它只是观察者模式的一个别称。但经过时间的沉淀,它已经强大起来,已经独立于观察者模式,成为一种新的设计模式。

在现在的发布 - 订阅模式中,发布者的消息不会直接发送给订阅者,这意味着发布者和订阅者都不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系 ,过滤所有发布者传入的消息,并相应的分发给它们的订阅者。
举个例子,你在微博上关注了 A,同时其他很多人也关注了 A,当 A 发布动态时,微博就会为你们推送这条动态。A 就是发布者,你就是订阅者,微博是调度中心,你和 A 是没有直接的消息往来的,全是通过微博来协调的。

观察者模式和发布 - 订阅模式有什么区别?

我们先来看下者两个模式的实现结构:

观察者模式: 观察者 (Observer) 直接订阅 (Subscribe) 主体 (Subject),而当主体被激活时,会触发(Fire Event) 观察者里的事件。

发布 - 订阅模式: 订阅者 (Subscriber) 把自己想订阅的事件注册 (Subscribe) 到调度中心 (Topic),当发布者(Publisher) 发布该事件 (Publish topic) 到调度中心,也就是该事件触发时,由调度中心统一调度 (Fire Event) 订阅者注册到调度中心的处理代码。

案例

观察者模式

// 有一家猎人公会,其中每个猎人都具体发布任务 (publish),订阅任务(subscribe) 的功能
// 它们都有一个订阅列表记录谁订阅了自己

// 定义一个猎人,包括姓名、级别、订阅列表
function Hunter(name, level) {
  this,name = name
  this.level = level
  this.list = []}
Hunter.prototype.publish = function (money) {console.log(this,level + '猎人:' + this.name + '寻求帮助')
  this.list.forEach(function (callback) {callback && callback(money)
  })
}
Hunter.prototype.subscribe = function (target, callback) {console.log(this.level + '猎人:' + this.name + '订阅了:' + target.name)
  target.list.push(callback)
}

// 猎人公会走注册了几个猎人
var hunterZhang = new Hunter('张三', '钻石')
var hunterLi = new Hunter('李四', '黄金')
var hunterWang = new Hunter('王五', '白银')
var hunterZhao = new Hunter('赵六', '青铜')

// 赵六等级较低,可能需要帮助,所以张三、李四、王五都订阅了赵六
hunterZhang.subscribe(hunterZhao, function (money) {console.log('小明表示:' + (money > 200 ? '':' 暂时很忙,不能 ') +' 给予帮助 ')
})
hunterLi.subscribe(hunterZhao, function () {console.log('李四表示: 给予帮助')
})
hunterWang.subscribe(hunterZhao, function () {console.log('王五表示: 给予帮助')
})

// 赵六遇到困难,悬赏 198 寻求帮助
hunterZhao.publish(198)

// 猎人们 (观察者) 关联他们感兴趣的猎人(目标对象),如赵六,当赵六有困难时,会自动通知给他们(观察者)

发布 - 订阅模式

// 定义了一家猎人公会
// 主要功能包含任务发布大厅(topics)、订阅任务(subscribe)、发布任务(publish)
var HunterUnion = {
  type: 'hunt',
  topics: Object.create(null),
  subscribe: function (topic, callback) {if (!this.topics[topic]) {this.topics[topic] = []}
    this.topics[topic].push(callback)
  },
  publish: function (topic, money) {if (!this.topics[topic]) {return}
    for(var cb of this.topics[topic]) {cb(money)
    }
  }
}

// 定义一个猎人类,包括姓名和级别
function Hunter(name, level) {
  this.name = name
  this.level = level
}
// 猎人可以在猎人公会发布、订阅任务
Hunter.prototype.subscribe = function (task, fn) {console.log(this.level + '猎人:' + this.name + '订阅了狩猎:' + task + '的任务')
  HunterUnion.subscribe(task, fn)
}
Hunter.prototype.publish = function (task, money) {console.log(this.level + '猎人:' + this.name + '发布了狩猎:' + task + '的任务')
  HunterUnion.publish(task, money)
}

// 猎人工会注册了几个猎人
let hunterZhang = new Hunter('张三', '钻石')
let hunterLi = new Hunter('李四', '黄金')
let hunterWang = new Hunter('王五', '白银')
let hunterZhao = new Hunter('赵六', '青铜')

// 张三,李四,王五分别订阅了狩猎 tiger 的任务
hunterZhang.subscribe('tiger', function(money){console.log('张三表示:' + (money > 200 ? '':' 不 ') +' 接取任务 ')
})
hunterLi.subscribe('tiger', function(money){console.log('李四表示:接取任务')
})
hunterWang.subscribe('tiger', function(money){console.log('王五表示:接取任务')
})
// 赵六订阅了狩猎 sheep 的任务
hunterZhao.subscribe('sheep', function(money){console.log('赵六表示:接取任务')
})

// 赵六发布了狩猎 tiger 的任务
hunterZhao.publish('tiger', 198)

// 猎人们发布 (发布者) 或订阅 (观察者 / 订阅者) 任务都是通过猎人工会 (调度中心) 关联起来的,他们没有直接的交流。

观察者模式和发布 - 订阅模式 最大的区别:发布 - 订阅模式有事件调度中心

观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,这种处理方式可能会造成代码的冗余。

发布 - 订阅模式中,统一由调度中进行处理,订阅者和发布者互不干扰,消除了发布者和订阅者之间的依赖。这样一方面实现了解耦,另一方面可以实现更加细粒度的控制。比如发布者发布了很多消息,但不是所有的订阅者都希望接收到,就可以在调度中心做一些处理,类似权限控制之类的。还可以做一些节流操作。

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

《JavaScript 设计模式与开发实践》一书中说到 分辨模式的关键是意图而不是结构

如果以 结构 来分辨模式,发布 - 订阅模式比观察者模式多了一个调度中心,所以发布 - 订阅模式不同于观察者模式。

如果以 意图 来分辨模式,它们都实现了对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知,并自动更新,那么它们就是同一种模式,发布 - 订阅模式是在观察者模式的基础上做了优化升级。

不过不管它们是不是同一个设计模式,它们的实现方式的确是有区别,我们在使用时应该根据应用场景来判断选择哪个。

正文完
 0