一、前言
观察者模式vs发布/订阅模式很容易混淆,像是凤梨和菠萝,傻傻分不清。Observer模式通常用Publish/Subscribe模式实现,我刚开始学习js的时候,以为这是同一回事,但是仔细学习,发现它们是有质的区别的。
二、观察者模式vs发布/订阅模式
1、观察者模式
2、发布/订阅模式
三、观察者模式
3.1 什么是观察者模式
官方:定义对象间的一对多的依赖关系,当一个对象发生改变时,所有依赖于他的对象都会受到通知。
通俗:一个对象(subject)维持一系列依赖它的对象(observer),将有关状态的任何变更自动通知它们(observer)
3.2 举个栗子
春招我喜欢A公司,于此同时1同学2同学3同学都喜欢,我们都在A公司的HR那留下简历,HR说一旦前端开发开始招聘就通知我和1同学2同学3同学。这里我和1同学2同学3同学(Observers)依赖A公司(Subject)的HR,一旦前端开发开始招聘(Event happen),HR就会通知(notify)我们(Observers)
3.3 两种主体,目标对象(Object)和观察者(Observer)
(1)目标对象Object
①维护observerList列表
②定义添加observer的方法
③当object自身发生变化,调用自己的notify()方法通知每个观察者执行 update() 方法
(2)观察者对象Observer
①update方法执行自定义的事件
②实现update供object对象调用
3.4 代码实现
简单版本的代码实现
class Observer {
/**
*构造器
* @param{Function} cb 回调函数,收到目标对象通知时执行
**/
constructor(cb) {
if (typeof cb === 'function') { //检测是否是函数类型
this.cb = cb
} else { //不是函数类型抛出异常
throw new Error('Observer构造器必须传入的是函数类型')
}
}
/*被目标对象object 通知notify时执行*/
update() {
this.cb()
}
}
//目标对象
class Subject {
constructor() {
//维护观察者表列
this.observerList = []
}
/**
* @param {Observer} observer Observer实例
**/
addObserver(observer) {
this.observerList.push(observer)
}
/**
* 通知所有观察者observer
**/
notify() {
this.observerList.forEach(observer => {
observer.update()
})
}
}
const observerCallback = function () {
console.log('I am be notified')
}
const observer = new Observer(observerCallback)
const subject = new Subject();
subject.addObserver(observer);
subject.notify();
3.5 观察者模式的特点
①角色分工明确,没有事件调度中心作为中间者。目标对象Subject和观察者Observer实现约定
②目标对象Subject和观察者Observer联系紧密。
四、发布/订阅模式
3.1 什么是发布/订阅模式
基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅主题的 Subscriber。
3.2 举个栗子
女生喜欢追剧,迫不及待的等《甄嬛传》更新,想在第一时间开始追最新集的《甄嬛传》,那么我该怎么办呢?我总不是抱着平板时时刻刻刷新吧。某个视频平台提供了订阅的功能,《甄嬛传》更新后,会第一时间通知你。
3.3 三个角色 发布者Publisher、事件调度中心 Event Channel、订阅者Subscriber
(1)任务发布者 —— Publisher
(2)事件调度中心——中介 Event Channel
①维护任务类型,以及每种情况下的订阅情况
②给订阅者提供订阅功能 subscribe
③给订阅者发布任务 publish
(3)订阅者(任务接受者)—Subscriber
3.4 代码实现
class PubSub {
constructor() {
// 维护事件及订阅行为
this.events = {}
}
/**
* 注册事件订阅行为
* @param {String} type 事件类型
* @param {Function} cb 回调函数
*/
subscribe(type, cb) {
if (!this.events[type]) {
this.events[type] = []
}
this.events[type].push(cb)
}
/**
* 发布事件
* @param {String} type 事件类型
* @param {...any} args 参数列表
*/
publish(type, ...args) {
if (this.events[type]) {
this.events[type].forEach(cb => {
cb(...args)
})
}
}
/**
* 移除某个事件的一个订阅行为
* @param {String} type 事件类型
* @param {Function} cb 回调函数
*/
unsubscribe(type, cb) {
if (this.events[type]) {
const targetIndex = this.events[type].findIndex(item => item === cb)
if (targetIndex !== -1) {
this.events[type].splice(targetIndex, 1)
}
if (this.events[type].length === 0) {
delete this.events[type]
}
}
}
/**
* 移除某个事件的所有订阅行为
* @param {String} type 事件类型
*/
unsubscribeAll(type) {
if (this.events[type]) {
delete this.events[type]
}
}
}
3.5 发布/订阅模式的特点
①发布者Publisher和订阅者Subscriber相互匿名,没有特殊的约束。通过事件调度中心Event Channel提供的接口发布和订阅事件
②松散耦合,灵活度高
③但事件越来越多的时候,会难以维护,需要考虑事件命名规范和避免数据流混乱
④用来处理不同系统组件的信息交流,也可以是非父子组件,类似vuex
五 观察者模式和发布/订阅模式的异同简单总结
设计模式 | 观察者模式 | 发布/订阅模式 |
---|---|---|
主体 | Object观察者、Subject目标对象 | Publisher发布者、Event Channel事件调度中心、Subscriber订阅者 |
主体关系 | Subject中通过observerList记录ObServer | Publisher和Subscribe互相匿名,通过事件调度中心联系 |
优点 | Subject和Object联系紧密,遵循规则方法 | 松散耦合,灵活度高,异步编程 |
缺点 | 紧耦合 | 当事件类型变多时,会增加维护成本 |
应用 | 数据双向绑定 | 事件总线EventBus |
(1)观察者模式中Object观察者和Subject目标对象互相知道对方;发布/订阅模式互相匿名,通过事件调度中心Event Channel中介进行信息通信代理。
(2)观察者模式组件时紧耦合;发布/订阅模式组件是松散耦合
(3)观察者模式大多数时候是同步的,事件发布/事件触发的时候,Subject就会去调用观察者模式;发布/订阅模式大多数是异步的,Publisher发布到消息通信代理中心,Subscriber去消息代理中心获取消息。
(4)观察者模式定义一对多的依赖关系,要求Observer必须subscribe订阅改变内容的事件
(5)观察者模式需要目标对象objectt和观察者observer关联在一起;发布/订阅模式需要借助管道,使用一个主题/事件通道,介于Publisher和Subscriber之间。
(6)观察者模式的Observer被动执行内容改变事件;发布/订阅模式的Subscriber可以自定义事件处理程序。
六、观察者模式应用和发布/订阅模式应用
子组件和父组件之间的通信,Vue中通过props完成父组件向子组件传递数据比如$ emit用来发布消息并且对
$on做统一处理。实现非父子组件通信,Vue中通过vuex,EventEmitter2
(1)DOM绑定事件—发布/订阅模式
document.querySelector('#btn').addEventListener('click',function () {
alert('You click this btn!Hello World!');
},false)
监听用户点击按钮的动作,但是不知道什么时候用户会点击按钮,所以需要去订阅click事件。
(2)数据双向绑定—观察者模式
双向绑定实现原理:数据劫持Object.defineProperty()、监听器Observer监听所有的属性,若发生变化告诉Watcher订阅者更新数据,解析器Compile解析对应的指令,执行更新的函数从而更新视图,实现双向绑定。
七、总结
在项目中,这两种模式有效果就好。这两种模式高度相似,没有必要强行区分理解他们。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16135.html