-
发布订阅和观察者模式
2020-04-20 17:30:54观察者模式和发布订阅模式的区别 在观察者模式的Subject就向一个发布者Publisher,观察者(Observer)和订阅者(Subscriber)关联。subject通知observers就像publisher发布一样。发布订阅模式从广义上来讲就是观察...观察者模式和发布订阅模式的区别
在观察者模式的Subject就向一个发布者Publisher,观察者(Observer)和订阅者(Subscriber)关联。subject通知observers就像publisher发布一样。发布订阅模式从广义上来讲就是观察者模式的一种实现,但是两者是有区别的:
- Publisher和Subscriber不知道彼此的存在,需要中间的消息调度组件,它把两者联系起来。也就是说Publisher只管发布消息,Subscriber只管订阅消息,他们不直接联系。
- Subject是知道Observer的,Subject一直保持对观察者的记录。
- 观察者模式大多数时候是同步的,当事件触发,Subject就会通知观察者的方法;发布订阅模式多数时候是异步的(消息队列)
- 观察者在单个应用程序地址空间,发布订阅更像在交叉应用的模式。
-
发布订阅和观察者模式的区别
2020-08-06 10:18:11观察者模式是观察者和被观察者之间的通信,而发布订阅模式中间增加了一个中转层,通过第三方来分发信息。 观察者模式 // Subject为被观察者,Subject中的状态(state)改变,就通知 Observer更新 class ...有些人认为观察者模式就是发布订阅模式,实际上观察者模式是包含了订阅发布模式,发布订阅模式只是观察者模式中的一种。观察者模式是观察者和被观察者之间的通信,而发布订阅模式中间增加了一个中转层,通过第三方来分发信息。
观察者模式
// Subject为被观察者,Subject中的状态(state)改变,就通知 Observer更新 class Subject { constructor() { this.observes = [] this.state = false } // this.observes存储观察者 attach(observe){ this.observes.push(observe) } // 状态改变,通知 Observer 触发更新 setState(newState){ this.state = newState this.observes.forEach( observer => observer.update(newState)) } } // Observer为观察者,观察Subject的状态是否改变 class Observer { constructor(name) { this.name = name } // 更新 update(state){ console.log(this.name + ",接收到了通知,被观察者的属性变为 " + state) } } var sub = new Subject() var obs1 = new Observer('观察者1') var obs2 = new Observer('观察者2') sub.attach(obs1) sub.attach(obs2) // 被观察者的状态改变,触发观察者更新 sub.setState(true)
vue中数据劫持中就用到了观察者模式,data中的属性一发生变化,就通知view界面更新,从而实现数据驱动,想要进一步了解vue底层原理,可以参考可以参考github上的一篇文章 MVVM实现
发布订阅模式
// 发布订阅 class Events { constructor() { this.sub = {} // 容器 } // 根绝不同 name,订阅对应函数 $on(name, fn) { const wrap = this.sub[name] || (this.sub[name] = []) wrap.push(fn) } // 遍历所有相同name的订阅函数,并发布 $emit(name, ...args) { const fns = this.sub[name] || [] fns.forEach(fn => { fn.apply(this, args) }) } // 销毁,避免内存泄漏 $of(name){ this.sub[name] = null } } // event 相当于中转器 const event = new Events() // 订阅 event.$on('eventname', function (a, b) { console.log(a, b) }) event.$on('eventname', function (a, b) { console.log(a, b) }) // 发布 event.$emit('eventname', 'a', 'b')
vue中的兄弟组件通信bus的原理就是发布订阅模式,该模式有个缺点,当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。所以该消息不使用的时候,调用$of销毁,以避免内存泄漏。
总结
总而言之,在观察者模式中,观察者(Observer)是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布订阅通过共享全局状态,往往更多执行异步事件~~~~。
-
java发布订阅模式简单实现,从一道面试题简单谈谈发布订阅和观察者模式
2021-03-23 19:22:03今天的话题是javascript中常被提及的「发布订阅模式和观察者模式」,提到这,我不由得想起了一次面试。记得在去年的一次求职面试过程中,面试官问我,“你在项目中是怎么处理非父子组件之间的通信的?”。我答道,...今天的话题是javascript中常被提及的「发布订阅模式和观察者模式」,提到这,我不由得想起了一次面试。记得在去年的一次求职面试过程中,面试官问我,“你在项目中是怎么处理非父子组件之间的通信的?”。我答道,“有用到vuex,有的场景也会用EventEmitter2”。面试官继续问,“那你能手写代码,实现一个简单的EventEmitter吗?”
手写EventEmitter
我犹豫了一会儿,想到使用EventEmitter2时,主要是用emit发事件,用on监听事件,还有off销毁事件监听者,removeAllListeners销毁指定事件的所有监听者,还有once之类的方法。考虑到时间关系,我想着就先实现发事件,监听事件,移除监听者这几个功能。当时可能有点紧张,不过有惊无险,在面试官给了一点提示后,顺利地写出来了!现在把这部分代码也记下来。
class EventEmitter {
constructor() {
// 维护事件及监听者
this.listeners = {}
}
/**
* 注册事件监听者
* @param {String} type 事件类型
* @param {Function} cb 回调函数
*/
on(type, cb) {
if (!this.listeners[type]) {
this.listeners[type] = []
}
this.listeners[type].push(cb)
}
/**
* 发布事件
* @param {String} type 事件类型
* @param {...any} args 参数列表,把emit传递的参数赋给回调函数
*/
emit(type, ...args) {
if (this.listeners[type]) {
this.listeners[type].forEach(cb => {
cb(...args)
})
}
}
/**
* 移除某个事件的一个监听者
* @param {String} type 事件类型
* @param {Function} cb 回调函数
*/
off(type, cb) {
if (this.listeners[type]) {
const targetIndex = this.listeners[type].findIndex(item => item === cb)
if (targetIndex !== -1) {
this.listeners[type].splice(targetIndex, 1)
}
if (this.listeners[type].length === 0) {
delete this.listeners[type]
}
}
}
/**
* 移除某个事件的所有监听者
* @param {String} type 事件类型
*/
offAll(type) {
if (this.listeners[type]) {
delete this.listeners[type]
}
}
}
// 创建事件管理器实例
const ee = new EventEmitter()
// 注册一个chifan事件监听者
ee.on('chifan', function() { console.log('吃饭了,我们走!') })
// 发布事件chifan
ee.emit('chifan')
// 也可以emit传递参数
ee.on('chifan', function(address, food) { console.log(`吃饭了,我们去${address}吃${food}!`) })
ee.emit('chifan', '三食堂', '铁板饭') // 此时会打印两条信息,因为前面注册了两个chifan事件的监听者
// 测试移除事件监听
const toBeRemovedListener = function() { console.log('我是一个可以被移除的监听者') }
ee.on('testoff', toBeRemovedListener)
ee.emit('testoff')
ee.off('testoff', toBeRemovedListener)
ee.emit('testoff') // 此时事件监听已经被移除,不会再有console.log打印出来了
// 测试移除chifan的所有事件监听
ee.offAll('chifan')
console.log(ee) // 此时可以看到ee.listeners已经变成空对象了,再emit发送chifan事件也不会有反应了
有了这个自己写的简单版本的EventEmitter,我们就不用依赖第三方库啦。对了,vue也可以帮我们做这样的事情。
const ee = new Vue();
ee.$on('chifan', function(address, food) { console.log(`吃饭了,我们去${address}吃${food}!`) })
ee.$emit('chifan', '三食堂', '铁板饭')
所以我们可以单独new一个Vue的实例,作为事件管理器导出给外部使用。想测试的朋友可以直接打开vue官网,在控制台试试,也可以在自己的vue项目中实践下哦。
发布订阅模式
其实仔细看看,EventEmitter就是一个典型的发布订阅模式,实现了事件调度中心。发布订阅模式中,包含发布者,事件调度中心,订阅者三个角色。我们刚刚实现的EventEmitter的一个实例ee就是一个事件调度中心,发布者和订阅者是松散耦合的,互不关心对方是否存在,他们关注的是事件本身。发布者借用事件调度中心提供的emit方法发布事件,而订阅者则通过on进行订阅。
如果还不是很清楚的话,我们把代码换下单词,是不是变得容易理解一点呢?
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]
}
}
}
画图分析
最后,我们画个图加深下理解:
特点
发布订阅模式中,对于发布者Publisher和订阅者Subscriber没有特殊的约束,他们好似是匿名活动,借助事件调度中心提供的接口发布和订阅事件,互不了解对方是谁。
松散耦合,灵活度高,常用作事件总线
易理解,可类比于DOM事件中的dispatchEvent和addEventListener。
缺点
当事件类型越来越多时,难以维护,需要考虑事件命名的规范,也要防范数据流混乱。
观察者模式
观察者模式与发布订阅模式相比,耦合度更高,通常用来实现一些响应式的效果。在观察者模式中,只有两个主体,分别是目标对象Subject,观察者Observer。
观察者需Observer要实现update方法,供目标对象调用。update方法中可以执行自定义的业务代码。
目标对象Subject也通常被叫做被观察者或主题,它的职能很单一,可以理解为,它只管理一种事件。Subject需要维护自身的观察者数组observerList,当自身发生变化时,通过调用自身的notify方法,依次通知每一个观察者执行update方法。
按照这种定义,我们可以实现一个简单版本的观察者模式。
// 观察者
class Observer {
/**
* 构造器
* @param {Function} cb 回调函数,收到目标对象通知时执行
*/
constructor(cb){
if (typeof cb === 'function') {
this.cb = cb
} else {
throw new Error('Observer构造器必须传入函数类型!')
}
}
/**
* 被目标对象通知时执行
*/
update() {
this.cb()
}
}
// 目标对象
class Subject {
constructor() {
// 维护观察者列表
this.observerList = []
}
/**
* 添加一个观察者
* @param {Observer} observer Observer实例
*/
addObserver(observer) {
this.observerList.push(observer)
}
/**
* 通知所有的观察者
*/
notify() {
this.observerList.forEach(observer => {
observer.update()
})
}
}
const observerCallback = function() {
console.log('我被通知了')
}
const observer = new Observer(observerCallback)
const subject = new Subject();
subject.addObserver(observer);
subject.notify();
画图分析
最后也整张图理解下观察者模式:
特点
角色很明确,没有事件调度中心作为中间者,目标对象Subject和观察者Observer都要实现约定的成员方法。
双方联系更紧密,目标对象的主动性很强,自己收集和维护观察者,并在状态变化时主动通知观察者更新。
缺点
我还没体会到,这里不做评价
结语
关于这个话题,网上文章挺多的,观点上可能也有诸多分歧。重复造轮子,纯属帮助自己加深理解。
本人水平有限,以上仅是个人观点,如有错误之处,还请斧正!如果能帮到您理解发布订阅模式和观察者模式,非常荣幸!
如果有兴趣看看我这糟糕的代码,请点击github,祝大家生活愉快!
关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[从一道面试题简单谈谈发布订阅和观察者模式]http://www.zyiz.net/tech/detail-97079.html
-
从一道面试题简单谈谈发布订阅和观察者模式
2019-12-12 19:06:12今天的话题是javascript中常被提及的「发布订阅模式和观察者模式」,提到这,我不由得想起了一次面试。今天的话题是
javascript
中常被提及的「发布订阅模式和观察者模式」,提到这,我不由得想起了一次面试。记得在去年的一次求职面试过程中,面试官问我,“你在项目中是怎么处理非父子组件之间的通信的?”。我答道,“有用到vuex
,有的场景也会用EventEmitter2
”。面试官继续问,“那你能手写代码,实现一个简单的EventEmitter
吗?”手写EventEmitter
我犹豫了一会儿,想到使用
EventEmitter2
时,主要是用emit
发事件,用on
监听事件,还有off
销毁事件监听者,removeAllListeners
销毁指定事件的所有监听者,还有once
之类的方法。考虑到时间关系,我想着就先实现发事件,监听事件,移除监听者这几个功能。当时可能有点紧张,不过有惊无险,在面试官给了一点提示后,顺利地写出来了!现在把这部分代码也记下来。class EventEmitter { constructor() { // 维护事件及监听者 this.listeners = {} } /** * 注册事件监听者 * @param {String} type 事件类型 * @param {Function} cb 回调函数 */ on(type, cb) { if (!this.listeners[type]) { this.listeners[type] = [] } this.listeners[type].push(cb) } /** * 发布事件 * @param {String} type 事件类型 * @param {...any} args 参数列表,把emit传递的参数赋给回调函数 */ emit(type, ...args) { if (this.listeners[type]) { this.listeners[type].forEach(cb => { cb(...args) }) } } /** * 移除某个事件的一个监听者 * @param {String} type 事件类型 * @param {Function} cb 回调函数 */ off(type, cb) { if (this.listeners[type]) { const targetIndex = this.listeners[type].findIndex(item => item === cb) if (targetIndex !== -1) { this.listeners[type].splice(targetIndex, 1) } if (this.listeners[type].length === 0) { delete this.listeners[type] } } } /** * 移除某个事件的所有监听者 * @param {String} type 事件类型 */ offAll(type) { if (this.listeners[type]) { delete this.listeners[type] } } } // 创建事件管理器实例 const ee = new EventEmitter() // 注册一个chifan事件监听者 ee.on('chifan', function() { console.log('吃饭了,我们走!') }) // 发布事件chifan ee.emit('chifan') // 也可以emit传递参数 ee.on('chifan', function(address, food) { console.log(`吃饭了,我们去${address}吃${food}!`) }) ee.emit('chifan', '三食堂', '铁板饭') // 此时会打印两条信息,因为前面注册了两个chifan事件的监听者 // 测试移除事件监听 const toBeRemovedListener = function() { console.log('我是一个可以被移除的监听者') } ee.on('testoff', toBeRemovedListener) ee.emit('testoff') ee.off('testoff', toBeRemovedListener) ee.emit('testoff') // 此时事件监听已经被移除,不会再有console.log打印出来了 // 测试移除chifan的所有事件监听 ee.offAll('chifan') console.log(ee) // 此时可以看到ee.listeners已经变成空对象了,再emit发送chifan事件也不会有反应了
有了这个自己写的简单版本的
EventEmitter
,我们就不用依赖第三方库啦。对了,vue
也可以帮我们做这样的事情。const ee = new Vue(); ee.$on('chifan', function(address, food) { console.log(`吃饭了,我们去${address}吃${food}!`) }) ee.$emit('chifan', '三食堂', '铁板饭')
所以我们可以单独
new
一个Vue
的实例,作为事件管理器导出给外部使用。想测试的朋友可以直接打开vue
官网,在控制台试试,也可以在自己的vue
项目中实践下哦。发布订阅模式
其实仔细看看,
EventEmitter
就是一个典型的发布订阅模式,实现了事件调度中心。发布订阅模式中,包含发布者,事件调度中心,订阅者三个角色。我们刚刚实现的EventEmitter
的一个实例ee
就是一个事件调度中心,发布者和订阅者是松散耦合的,互不关心对方是否存在,他们关注的是事件本身。发布者借用事件调度中心提供的emit
方法发布事件,而订阅者则通过on
进行订阅。如果还不是很清楚的话,我们把代码换下单词,是不是变得容易理解一点呢?
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] } } }
画图分析
最后,我们画个图加深下理解:
特点
- 发布订阅模式中,对于发布者
Publisher
和订阅者Subscriber
没有特殊的约束,他们好似是匿名活动,借助事件调度中心提供的接口发布和订阅事件,互不了解对方是谁。 - 松散耦合,灵活度高,常用作事件总线
- 易理解,可类比于
DOM
事件中的dispatchEvent
和addEventListener
。
缺点
- 当事件类型越来越多时,难以维护,需要考虑事件命名的规范,也要防范数据流混乱。
观察者模式
观察者模式与发布订阅模式相比,耦合度更高,通常用来实现一些响应式的效果。在观察者模式中,只有两个主体,分别是目标对象
Subject
,观察者Observer
。- 观察者需
Observer
要实现update
方法,供目标对象调用。update
方法中可以执行自定义的业务代码。 - 目标对象
Subject
也通常被叫做被观察者或主题,它的职能很单一,可以理解为,它只管理一种事件。Subject
需要维护自身的观察者数组observerList
,当自身发生变化时,通过调用自身的notify
方法,依次通知每一个观察者执行update
方法。
按照这种定义,我们可以实现一个简单版本的观察者模式。
// 观察者 class Observer { /** * 构造器 * @param {Function} cb 回调函数,收到目标对象通知时执行 */ constructor(cb){ if (typeof cb === 'function') { this.cb = cb } else { throw new Error('Observer构造器必须传入函数类型!') } } /** * 被目标对象通知时执行 */ update() { this.cb() } } // 目标对象 class Subject { constructor() { // 维护观察者列表 this.observerList = [] } /** * 添加一个观察者 * @param {Observer} observer Observer实例 */ addObserver(observer) { this.observerList.push(observer) } /** * 通知所有的观察者 */ notify() { this.observerList.forEach(observer => { observer.update() }) } } const observerCallback = function() { console.log('我被通知了') } const observer = new Observer(observerCallback) const subject = new Subject(); subject.addObserver(observer); subject.notify();
画图分析
最后也整张图理解下观察者模式:
特点
- 角色很明确,没有事件调度中心作为中间者,目标对象
Subject
和观察者Observer
都要实现约定的成员方法。 - 双方联系更紧密,目标对象的主动性很强,自己收集和维护观察者,并在状态变化时主动通知观察者更新。
缺点
我还没体会到,这里不做评价
结语
关于这个话题,网上文章挺多的,观点上可能也有诸多分歧。重复造轮子,纯属帮助自己加深理解。
本人水平有限,以上仅是个人观点,如有错误之处,还请斧正!如果能帮到您理解发布订阅模式和观察者模式,非常荣幸!
如果有兴趣看看我这糟糕的代码,请点击github,祝大家生活愉快!
- 发布订阅模式中,对于发布者
-
2.41.2-发布订阅和观察者模式
2020-04-05 11:26:31// 发布订阅 发布和订阅没关系 中间通过数组进行关联 function Events() { this.callbacks = []; //接收很多函数 this.results = []; } // 订阅 Events.prototype.on = function(callback) { //传一个函数 t... -
promise - 3.发布订阅和观察者模式
2020-06-30 15:55:481. 发布订阅 大白话解释 订阅好一件事,当这件事发生的时候,触发对应的函数 订阅: on 发布:emit promise 内部也是基于发布订阅的 好处 多个类之间可以解耦合 特点 订阅方和发布方没有任何的关系 ... -
【10分钟带你了解发布订阅和观察者模式】
2019-03-16 11:46:04发布订阅模式 --废话不多说直接上代码 发布和订阅是没有关系的他们是靠中介产生关系的,把需要订阅用的方法先存储起来,需要的时候再取出来发布 let fs = require('fs'); // 利用node的API fs模块异步读取文件模拟... -
发布订阅模式和观察者模式
2021-03-02 02:03:22发布订阅模式和观察者模式发布订阅模式观察者模式发布订阅模式 VS 观察者模式 发布订阅模式 订阅者、发布者、信号中心 // 具体实现可参考:https://blog.csdn.net/baidu_33591715/article/details/114258885 // ... -
发布订阅模式和观察者模式区别
2020-04-06 16:00:48一 观察者模式 观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动...三 观察者模式与发布订阅模式区别 四 观察者模式与发布订阅模式优缺点 ... -
java观察者订阅模式_观察者模式和发布订阅模式的区别
2021-03-12 23:23:26之前一直对观察者模式和发布订阅模式的区别理解不深,正好这段时间在看vue源码的分析,vue数据双向绑定也用到了发布订阅模式,于是又把这两者探究了一番,今天做个笔记加强印象。观察者模式和发布订阅模式最大的区别... -
java订阅发布模式_观察者模式和发布订阅模式有什么不同?
2021-03-06 20:04:23发布订阅模式属于广义上的观察者模式发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式发布订阅模式多了个事件通道在观察者模式中,观察者需要直接订阅目标事件;... -
观察者模式和发布订阅模式区别
2021-03-24 17:58:10观察者模式和发布订阅模式区别观察者模式和发布订阅模式区别:一、从表面上看:二、往更深层次讲:三、从使用层面上讲:四、总结: 观察者模式和发布订阅模式区别: 一、从表面上看: 观察者模式里,只有两个角色 ... -
javascript 发布订阅模式和观察者模式
2020-09-15 21:43:55发布订阅模式 三要素:订阅者,发布者,信号中心 我们假定存在一个信号中心,某个任务完成就向信号中心发布一个信号,其他任务可以向这个信号订阅这个模式,从而知道自己什么时候可以开始执行,这样的模式叫做发布... -
发布订阅者模式和观察者模式
2019-06-20 16:29:001、发布订阅者模式 //订阅发布平台应该是一个全局的 var pubsub={}; (function(pubsub){ var topics={}; pubsub.publish=function(topic){ //发布话题,订阅了该话题的都会收到消息 if(!topics[topic])... -
谈谈观察者模式和发布订阅模式
2019-09-23 14:17:22在网上看到许多关于观察者模式和发布订阅模式的博文,发现很多人都认为观察者模式即发布订阅模式,经过进一步的学习和理解,我认为观察者模式和发布订阅模式还是有一些区别的,下面谈谈我对观察者模式和发布订阅模式... -
kafka redis vs 发布订阅_观察者模式 vs 发布订阅模式
2020-12-24 09:48:39有一回面试,面试官问:观察者模式,和发布订阅模式,有什么区别?我脑海中立刻闪现了《Head First设计模式》里讲的:Publishers + Subscribers = Observer Pattern“哼哼,我知道答案了,兄dei!”,我内心无比鸡冻... -
观察者模式和发布订阅模式
2018-12-18 14:54:31发布订阅模式相比观察者模式多了个事件通道,订阅者和发布者不是直接关联的。 比如你去外面吃饭,就经常出现两种模式 1.观察者模式:你进入餐馆,坐在餐馆里等待叫号,当叫号时,你就知道又有人可以吃饭了,你会下...