当前位置: 首页 > news >正文

观察者模式与发布订阅模式

前言

我的任督二脉终于被打通了,现在该你了

区别

观察者模式

  1. 就2个角色:观察者和被观察者(重要)
  2. 明确知道状态源,明确知道对方是谁
  3. 一对多关系

发布订阅模式

  1. 有3个角色:发布者,订阅者和发布订阅中心(重要)
  2. 发布者和订阅者不知对方存在
  3. 多对多关系

观察者模式

  1. 观察者盯着被观察者看
  2. 被观察者将有权限添加,删除和通知观察者(这是被观察者至少需要的三个基本方法)
  3. 只要被观察者 发动 通知观察者方法时,观察者列表里的所有观察者就可以接收到消息

(一)实现

1. 被观察者(有一个观察者列表,可以添加,移除和通知观察者)

class Subject {constructor(){// 定义一个观察者列表用来存放观察者们this.observerList = [];}// 添加观察者    addObserver(observer){this.observerList.push(observer);}// 移除观察者removeObserver(observer){this.observerList.filter(o => o.name !== observer.name)}// 通知观察者notifyObserver(message){// 我理解的 notified 是观察者自己定义的如何通知自己,// 每个观察者的notified内部实现可能是不一样的this.observerList.forEach(o => o.notified(message))}}

2. 观察者(可以申请加入被观察者的列表,自定义通知方法)

class Observer {constructor(name, subject){this.name = name;if(subject){// 如果构建时就有了被观察者(subject),// 则申请让被观察者将自己加入列表subject.addObserver(this)}}notified(message){// TODO 观察者拿到订阅到的消息后要做的事console.log(this.name, '拿到了消息',message)}}

3. 使用

// 生成一个被观察者const subject = new Subject();// 生成一个自带subject的观察者const A_observer = new Observer('A',subject);// 通知subject.notifyObserver('咳咳');
// A拿到了消息咳咳// 生成一个没有观察对象subject的观察者const B_observer = new Observer('B',null);// 申请观察subjectsubject.addObserver(B_observer);
// 移除// subject.removeObserver(A_observer);
// 通知subject.notifyObserver('哈哈哈')    
// A拿到了消息哈哈哈
// B拿到了消息哈哈哈
// 当然也可以写一个新的class Observer来实现不同的notified

参考https://juejin.cn/post/6978728619782701087

(二)升级版实现

1. 被观察者

class newSubject {constructor(){this.observerList = [];}// 移除观察者removeObserver(observer){this.observerList.filter(o => o.name !== observer.name)}
__________修改___________________________________________________________________// 添加观察者addObserver(name, func){this.observerList.push({ name:name, callback:func});}// 通知观察者notifyObserver(message){// 我理解的 notified 是观察者自己定义的一个回调函数,// 即被观察者(observer)给观察者(subject)一个notified函数,告诉subject有消息就用这个函数通知自己,// 每个观察者的notified内部实现可能是不一样的// this.observerList.forEach(o => o.notified(message))// 通过对象的解构赋值,这样就可以不用规定死observer内部通知方法的名称为notified,更加灵活this.observerList.forEach(({name,callback}) => callback(message))}
}

2. 使用

    const new_subject = new newSubject();//添加观察者C时,将new_subject的观察者列表修改为统一的 { name, callback } 格式new_subject.addObserver('C',(message)=>{// notifiedconsole.log('hello, C', message)})//通知new_subject.notify(‘升级啦’)//‘hello, C升级啦’

参考http://dennisgo.cn/Articles/DesignPatterns/PubSub.html

一旦调用了subject.notify , 所有列表内的观察者都会收到通知,而且收到的是同样的消息

发布订阅模式

  1. 如之前所说,发布订阅模式有3个角色,发布者,订阅者和发布订阅中心,
  2. 发布订阅核心是基于发布订阅中心来建立联系
  3. 发布者无需关心订阅者有哪些,订阅者也无需关心有哪些发布者,可以表达对一个或多个主题的兴趣,只接收感兴趣的消息

让我们来想象一下邮件系统,你可以作为订阅者订阅某个网站的通知,邮件系统在其中充当发布订阅中心的角色,而发布者则是你订阅的网站。

整个链路是从你的订阅开始,你的订阅动作是在某个你想订阅的网站填入自己的邮箱(在主题中添加订阅者列表),并在邮箱内记录这个网站信息(添加主题),后续当网站有内容更新时,邮件系统会及时接收到并向你发送邮件。
————————————————引用自掘金 https://juejin.cn/post/6978728619782701087

(一)实现

1. 发布订阅中心 (记住订阅是在为主题添加订阅者,发布是在执行订阅者的通知方法)

// 发布订阅中心
class PubSub {constructor() {this.sublist = {}; //为什么观察者模式要用数组,这里却要用对象?想想平时在设置变量时,一对多的关系一般用数组来表示,而多对多的关系一般都用//数组对象来表示是不是//订阅列表,每个主题对应对象中的一个数组,键为主题名,值为不同订阅者的通知方式(可以理解为该主题下的订阅者列表)//为每个主题添加了多个订阅者,存放在数组(订阅者列表)中/*** this.sublist = {*     theme1:[notify_1_1, notify_1_2], // notify_1_1订阅了theme1,notify_1_2订阅了theme1,*     theme2:[notify_1_1, notify_2_2], // notify_1_1订阅了theme2,notify_2_2订阅了theme2,*     ......* }* **/}// 订阅subscribe(theme, notify_fn) {// 如果this.sublist[theme]为null,先将其定义为数组, 有了主题后,直接push订阅者的通知方法即可(this.sublist[theme] || (this.sublist[theme] = [])).push(notify_fn);}// 发布publish(theme, ...args) {if(!this.sublist[theme]){// 如果该主题不存在通知方法(即订阅者)return;}// 发布某个主题时,该主题的所有订阅者都可以接到通知this.sublist[theme].map(notify_fn => notify_fn(args))}}

2. 使用

 const pubsub = new PubSub();// 订阅者function user1(content) {console.log('用户1订阅 ',content)}function user2(content) { console.log('用户2订阅 ',content)}function user3(content) {console.log('用户3订阅 ',content)}// 添加订阅pubsub.subscribe('theme1',(content)=>{// 以后如果发布订阅中心pubsub发布了theme1的内容,立即通知执行user1方法user1(content)})pubsub.subscribe('theme1',(content)=>{user2(content)})pubsub.subscribe('theme2',(content)=>{user3(content)})// 发布(发布即起到了通知的作用,只有发布订阅中心执行发布方法,订阅者才能收到消息,因为publish执行了notify_fn)pubsub.publish('theme1', '主题1内容');pubsub.publish('theme2', '主题2内容');// 结果// 用户1订阅 主题1内容// 用户2订阅 主题1内容// 用户3订阅 主题2内容

参考
https://extremej.itscoder.com/different_between_observe_and_publish/
http://dennisgo.cn/Articles/DesignPatterns/PubSub.html
https://juejin.cn/post/6844903842501378055
https://juejin.cn/post/6844903850105634824

实际场景

  1. Node.js中自带的EventEmiter模块
  2. Vue.js中数据响应式的实现
  3. watch、watcher、observe、observer、listen、listener、dispatch、trigger、emit、on、event、eventbus、EventEmitter这类单词出现的地方,很有可能是在使用观察者模式或发布订阅模式
  4. 例如on (subscribe),和 emit(publish)

总结

1. 角色对象

  • 观察者模式角色:观察者和被观察者
  • 发布订阅模式角色:发布者,订阅者和发布订阅中心

2. 实现

  • 观察者模式:
    • 被观察者(有一个观察者列表,可以添加,移除和通知观察者)
    • 观察者(可以申请加入被观察者的列表,自定义通知方法)
  • 发布订阅模式:
    • 发布订阅中心 (记住订阅是在为主题添加订阅者,发布是在执行订阅者的通知方法)

3. 特定消息接收

  • 发布订阅模式可以自定义需要接受的通知,定制主题
  • 观察者模式相当于是无差别广播,一旦调用了notify通知 , 所有列表内的观察者都会收到通知,而且收到的是同样的消息

4. 映射关系

  • 观察者模式:普遍一对多
  • 发布订阅模式:普遍多对多

5. 对象间关系

  • 观察者模式:明确知道状态源,双方直接联系
  • 发布订阅者模式:双方通过发布订阅中心关联
http://www.lryc.cn/news/38686.html

相关文章:

  • 磨金石教育摄影技能干货分享|烟花三月下扬州,是时候安排了!
  • Kafka 消费组位移
  • Python|数学|贪心|数组|动态规划|单选记录:实现保留3位有效数字(四舍六入五成双规则)|用Python来创造一个提示用户输入数字的乘法表|最小路径和
  • 【MySQL】MySQL的索引
  • 弱监督实例分割 Box-supervised Instance Segmentation with Level Set Evolution 论文笔记
  • Springboot是什么
  • LeetCode 134. 加油站(函数图像法 / 贪心)
  • 王道计算机组成原理课代表 - 考研计算机 第三章 存储系统 究极精华总结笔记
  • Flask-mock接口数据流程
  • springboot项目配置序列化,反序列化器
  • c++11 标准模板(STL)(std::unordered_map)(九)
  • Seay代码审计工具
  • 界面开发(4)--- PyQt5实现打开图像及视频播放功能
  • 核心系统国产平台迁移验证
  • 【数据结构之二叉树】——二叉树的概念及结构,特殊的二叉树和二叉树性质
  • Android学习之帧动画和视图动画
  • vue2和vue3的区别
  • 【你不知道的事】JavaScript 中用一种更先进的方式进行深拷贝:structuredClone
  • XE开发Linux应用(二)-Webservice
  • kubernetes实战与源码学习
  • CNCF x Alibaba云原生技术公开课 第八章 应用配置管理
  • YUV实践记录
  • 【题解】百度2020校招Web前端工程师笔试卷(第一批):单选题、多选题
  • 探索云原生技术之容器编排引擎-kubeadm安装kubernetes1.21.10(新版:针对高版本内核)
  • 2023广西自治区职业技能大赛“网络安全” 项目比赛任务书
  • Reactor模式
  • Git图解-IDEA中的Git操作
  • 在一个web应用中应该如何完成资源的跳转
  • 前缀和部分题目
  • 三天吃透MySQL面试八股文