javascript 设计模式之发布订阅者模式
2021/5/12 20:25:28
本文主要是介绍javascript 设计模式之发布订阅者模式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
上一篇讲到观察者模式,这篇要讲下发布-订阅模式。
有人可能会说了:这两个不是一回事吗?确实这两个模式的核心思想、运行机制上没有本质的差别。
但还是有些差别,要不然我这篇要讲啥,且听我娓娓道来
本文代码
什么是发布订阅者模式
基于一个主题/事件通道,希望接收通知的对象(称为subscriber)通过自定义事件订阅主题,被激活事件的对象(称为publisher)通过发布主题事件的方式被通知。
生活中的发布订阅者模式
还记得上次那个小明、小红、小二去楼盘买房的事?
小明最近看中了一个楼盘,到了售楼处之后才被告知,该楼盘的房子还未开盘,具体开盘时间还没定。除了小明,一起来的还有小红,小二人,为了第一时间得知楼盘开盘时间,他们都把电话号码留给了销售员王五,王五也答应楼盘一开盘就会发消息通知他们。
过了不久,新楼盘开盘时间定了,王五赶紧拿出花名册,遍历上面的号码,群发了短信通知了他们。
至于这些人收到通知是选择走路来、开车来、或者不来就自行决定了
这个是观察者模式。
如果小明等人没有去售楼处找五五,而是去该楼盘官网查看,得知还没开盘关注了该楼盘进展。开盘之后,王五也不会群发短信,而是将消息同步到官网,这样官网自动通知了关注该楼盘的意向购房者。
这样就是发布订阅者模式。
重点概念对号入座
发布者: 王五
订阅者: 小明、小红,小二
事件中心: 官网
订阅事件: 小明、小红、小二进入官网关注楼盘进展
发布事件: 官网自动通知了关注楼盘的意向购买者
这两者有啥差别呢?
- 发布订阅者模式,发布者跟订阅者之间并没有直接依赖,都是通过事件中心来处理。
- 发布订阅者模式,所有事件的发布/订阅操作,必须经由事件中心,禁止一切“私下交易”
手写发布订阅者模式
类比官网,事件中心应该要有发事件( emit ),监听事件( on ),还有销毁事件监听者( off ),销毁指定事件的所有监听者( offAll ),还有只触发一次( once )的功能
class EventEmitter { constructor() { this.handleEvents = {} } on(type, callback) { if (!this.handleEvents[type]) { this.handleEvents[type] = [] } this.handleEvents[type].push(callback) } emit(type, ...args) { if (this.handleEvents[type]) { this.handleEvents[type].forEach((callback) => { callback(...args) }) } } off(type, callback) { const callbacks = this.handleEvents[type] const index = callbacks.indexOf(callback) if (index !== -1) { callbacks.splice(index, 1) } if (callbacks.length === 0) { delete this.handleEvents[type] } } offAll(type) { if (this.handleEvents[type]) { delete this.handleEvents[type] } } once(type, callback) { const wrapper = (...args) => { callback.apply(args) this.off(type, wrapper) } this.on(type, wrapper) } }
测试
// 创建事件管理器实例 const emitter = new EventEmitter() // 注册一个refresh事件监听者 emitter.on('refresh', function () { console.log('调用后台获取最新数据') }) // 发布事件refresh emitter.emit('refresh') // 也可以emit传递参数 emitter.on('refresh', function (pageNo, pageSize) { console.log(`调用后台获取参数,参数为:{pageNo:${pageNo},pageSize:${pageSize}}`) }) emitter.emit('refresh', '2', '20') // 此时会打印两条信息,因为前面注册了两个refresh事件的监听者 // 测试移除事件监听 const toBeRemovedListener = function () { console.log('我是一个可以被移除的监听者') } emitter.on('testRemove', toBeRemovedListener) emitter.emit('testRemove') emitter.off('testRemove', toBeRemovedListener) emitter.emit('testRemove') // 此时事件监听已经被移除,不会再有console.log打印出来了 // 测试移除refresh的所有事件监听 emitter.offAll('refresh') console.log(emitter) // 此时可以看到emitter.handleEvents已经变成空对象了,再emit发送refresh事件也不会有反应了 emitter.once('onlyOne', function () { console.info('只会触发一次') }) emitter.emit('onlyOne') emitter.emit('onlyOne') // 不会弹出信息
发布订阅模式使用场景
事件监听
// 订阅这些函数,当点击时触发发布,会依次触发这些函数 $('#btn1').click(function () { alert(1) }) $('#btn1').click(function () { alert(2) }) $('#btn1').click(function () { alert(3) })
promise
promise 就是观察者模式的经典场景:
先将 then 里面的函数储存起来,在 resovle 与 reject 里取出函数,并执行函数。
其实就是收集依赖->触发通知->取出依赖执行的方式
具体可以看手写 promise
jQuery.Callbacks
// 自定义事件,自定义回调 var callbacks = $.Callbacks() // 注意大小写, C 为大写 callbacks.add(function (info) { console.info('fn1', info) }) callbacks.add(function (info) { console.info('fn2', info) }) callbacks.add(function (info) { console.info('fn2', info) }) callbacks.fire('come') callbacks.fire('go')
node 里的 event 事件
const EventEmitter = require('events').EventEmitter const emitter1 = new EventEmitter() emitter1.on('some', (name) => { // 监听 some 事件 console.info('some event is occured 1 ' + name) }) emitter1.on('some', () => { // 监听 some 事件 console.info('some event is occured 2') }) // 触发 some 事件 // 并传递参数 emitter1.emit('some', 'zhangsan')
node 里的 stream
// Steam 用到了自定义事件 const fs = require('fs') var readStream = fs.createReadStream('./data/1.js') var len = 0 readStream.on('data', (chunk) => { len += chunk.toString().length }) readStream.on('end', () => { console.info('总共多少个字符:' + len) })
node 里的 readLine
// readLine 用到了自定义事件 const fs = require('fs') const readLine = require('readline') var rl = readLine.createInterface({ input: fs.createReadStream('./data/1.js') }) var lineNum = 0 rl.on('line', (line) => { lineNum++ }) rl.on('close', () => { console.info('lineNum:' + lineNum) })
Vue 里的 Event BUS
创建一个 Event Bus(本质上也是 Vue 实例)并导出:
const EventBus = new Vue() export default EventBus
在主文件里引入EventBus,并挂载到全局:
import bus from 'EventBus的文件路径' Vue.prototype.bus = bus
订阅事件:
// 这里func指someEvent这个事件的监听函数 this.bus.$on('someEvent', func)
发布(触发)事件:
// 这里params指someEvent这个事件被触发时回调函数接收的入参 this.bus.$emit('someEvent', params)
总结
观察者模式:
- 角色很明确,没有事件调度中心作为中间者,目标对象Subject和观察者Observer都要实现约定的成员方法。
- 双方联系更紧密,目标对象的主动性很强,自己收集和维护观察者,并在状态变化时主动通知观察者更新。
发布订阅者模式:
- 发布订阅模式中,发布者不直接触及到订阅者、而是由统一的第三方来完成实际的通信的操作,互不关心对方是谁。
- 松散耦合,灵活度高,常用作事件总线
看的出来,发布订阅者模式更加高级,因为它更加松散,没有耦合,那是不是现实中发布订阅者模式用的更多呢?实际上不是的。因为在实际开发中,我们的模块解耦诉求并不是要求它们完全解耦,如果两个模块之间本身存在关联,且这种关联是稳定的、必要的,那这时就应该用观察者模式即可。
参考链接
JavaScript 设计模式核⼼原理与应⽤实践
从一道面试题简单谈谈发布订阅和观察者模式
结语
你的点赞是对我最大的肯定,如果觉得有帮助,请留下你的赞赏,谢谢!!!
这篇关于javascript 设计模式之发布订阅者模式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-06小米11i印度快充版ROM合集:极致体验,超越期待
- 2024-10-06【ROM下载】小米11i 5G 印度版系统, 疾速跃迁,定义新速度
- 2024-10-06【ROM下载】小米 11 青春活力版,青春无极限,活力全开
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求