前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >谈谈观察者模式和发布订阅模式

谈谈观察者模式和发布订阅模式

作者头像
Ewall
发布于 2020-04-10 08:02:58
发布于 2020-04-10 08:02:58
1.2K03
代码可运行
举报
文章被收录于专栏:vue学习vue学习
运行总次数:3
代码可运行

这两个东西捋清楚需要点耐心。

1、观察者vs发布订阅

  • 首先观察者跟发布订阅这两者虽然看起来很相似,但是两者是有差异的,至少从实现方式上就有差异。
  • 其次,虽然两者有差异,但是说发布订阅模式是观察者模式的变异也是ok的,因为它们思想上是一致的。
  • 最后,做个类比,观察者模式类似于房东—租客,发布订阅模式类似于房东—中介—租客,本文将围绕这两个模型来探讨实现。

观察者vs发布订阅

  • 下面我们就用结合代码来捋一捋。

2、观察者模式

  • 既然我们把观察者模式类比为房东——用户的这种关系模型,那我们就来想象一下,这个房东是某拆迁户,名下出租的房子有别墅(bigHouse)洋房(mediumHouse)平房(smallHouse)等三种户型出租,他微信里面有很多租客,分有钱的、没钱的等等,这些是前提。
  • 首先我们来定义一下租客。(你先不管里面为什么要写这些方法属性,后面把这个对象实例化变成个人以后会阐述的,你先看,看完了再回头review一下)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 观察者(租客)
class Observer {
  constructor(subject) {
    this.subject = subject;
  }
  notify() {
    console.log(`收到一条房东的消息,${this.subject}空了!!!`);
  }
}
  • 然后我们需要描述下房东:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 主题(房东)
class Subject {
  // 根据户型的不同收集相应的订阅者
  constructor() {
    this.subjectList = {};
  }
  // 订阅
  add(subject, observer) {
    if (!this.subjectList[subject]) {
      this.subjectList[subject] = [];
    }
    this.subjectList[subject].push(observer);
  }
  // 解除订阅
  remove(subject, observer) {
    this.subjectList[subject].forEach((item, index) => {
      if (item === observer) {
        this.subjectList[subject].splice(index, 1);
      }
    });
  }
  // 触发事件
  fire(subject) {
    this.subjectList[subject].forEach((item) => item.notify());
  }
}
  • Ok,接下来有这么三位观察者租客ABC,他们分别想租别墅(bigHouse)洋房(medium)平房(small)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 不同的用户想关注别墅(big)、`洋房(medium)`...等不同的户型
const observerA = new Observer("bigHouse");
const observerB = new Observer("mediumHouse");
const observerC = new Observer("smallHouse");
  • 当ABC这三个租客向房东表达了他们的意向以后,房客加他们微信的时候就对其分了不同的组:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 把这3个观察者添加到相应的分组
const subjects = new Subject();
subjects.add("bigHouse", observerA);
subjects.add("mediumHouse", observerB);
subjects.add("smallHouse", observerC);
  • 某一天,别墅空了,于是房东对别墅组的小伙伴们群发了一条消息,于是你就收到了推送;又某一天,洋房空了。。。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 某一天
subjects.fire("bigHouse"); // 收到一条房东的消息,bigHouse空了!!!
// 又某一天
subjects.fire("mediumHouse"); // 收到一条房东的消息,mediumHouse空了!!!
// 再某一天
subjects.fire("smallHouse"); // 收到一条房东的消息,smallHouse空了!!!
  • 这就是观察者模式,完整代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 观察者(租客)
class Observer {
  constructor(subject) {
    this.subject = subject;
  }
  notify() {
    console.log(`收到一条房东的消息,${this.subject}空了!!!`);
  }
}

// 主题(房东)
class Subject {
  // 根据户型的不同收集相应的订阅者
  constructor() {
    this.subjectList = {};
  }
  // 订阅
  add(subject, observer) {
    if (!this.subjectList[subject]) {
      this.subjectList[subject] = [];
    }
    this.subjectList[subject].push(observer);
  }
  // 解除订阅
  remove(subject, observer) {
    this.subjectList[subject].forEach((item, index) => {
      if (item === observer) {
        this.subjectList[subject].splice(index, 1);
      }
    });
  }
  // 触发事件
  fire(subject) {
    this.subjectList[subject].forEach((item) => item.notify());
  }
}

// 不同的用户想关注别墅(big)、`洋房(medium)`...等不同的户型
const observerA = new Observer("bigHouse");
const observerB = new Observer("mediumHouse");
const observerC = new Observer("smallHouse");

// 把这3个观察者添加到相应的分组
const subjects = new Subject();
subjects.add("bigHouse", observerA);
subjects.add("mediumHouse", observerB);
subjects.add("smallHouse", observerC);

// 某一天
subjects.fire("bigHouse"); // 收到一条房东的消息,bigHouse空了!!!
// 又某一天
subjects.fire("mediumHouse"); // 收到一条房东的消息,mediumHouse空了!!!
// 再某一天
subjects.fire("smallHouse"); // 收到一条房东的消息,smallHouse空了!!!

3、发布订阅模式

(1)理解

  • 当你了解了观察者模式房东—租客这种模型以后,你会发现,如果观察者很多,那么房东压力还是挺大的,比如收钱的压力。
  • 这个时候,房东每天签合同、收房租跑断腿,不堪其扰,于是就去拜托中介,交给中介打理省心,于是就有了类似于房东—中介—租客的这种发布订阅模式。

(2)实践

  • 首先我们需要定义一下中介机构
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class EventBus {
  constructor() {
    // 定义事件池
    // 数据结构应该长这样:{<事件队列名>:<要执行的事件队列>}
    // {'buy': ['func1','func2','func3']}
    this.events = {};
  }

  /**
   * 订阅
   * @param {事件队列名} name
   * @param {事件函数} func
   */
  on(name, func) {
    if (!this.events[name]) {
      this.events[name] = [];
    }
    this.events[name].push(func);
  }

  /**
   * 发布
   * @param {事件队列名} args
   * @param {传入参数} args
   */
  emit(name, ...args) {
    if (!this.events[name]) {
      return;
    }
    this.events[name].forEach(item => {
      item.apply(this, args);
    });
  }

  /**
   * 删除
   * @param {事件队列名} args
   */
  remove(name) {
    if (this.events[name]) {
      delete this.events[name];
    }
  }
}
  • 然后租客通过中介机构根据自己的需要订阅了不同的房型。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 实例化
const bus = new EventBus();

// 不同租客订阅了不同需求房型
bus.on("bigHouse", function (value) {
  console.log(`A收到了一条消息:${value}`);
});

bus.on("mediumHouse", function (value) {
  console.log(`B收到了一条消息:${value}`);
});

bus.on("smallHouse", function (value) {
  console.log(`C收到了一条消息:${value}`);
});
  • 某一天,房东就直接通过中介发消息了,说:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 某天房东通过中介发布了一条消息
bus.emit("bigHouse", "别墅有房了"); // A收到了一条消息:别墅有房了
// 又某一天
bus.emit("mediumHouse", "洋房有房了"); // B收到了一条消息:洋房有房了
// 再某一天。。。。
bus.emit("smallHouse", "小平层有房了"); // C收到了一条消息:小平层有房了
  • 完整代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class EventBus {
  constructor() {
    // 定义事件池
    // 数据结构应该长这样:{<事件队列名>:<要执行的事件队列>}
    // {'say': ['func1','func2','func3']}
    this.events = {};
  }

  /**
   * 订阅
   * @param {事件队列名} name
   * @param {事件函数} func
   */
  on(name, func) {
    if (!this.events[name]) {
      this.events[name] = [];
    }
    this.events[name].push(func);
  }

  /**
   * 发布
   * @param {事件队列名} args
   * @param {传入参数} args
   */
  emit(name, ...args) {
    if (!this.events[name]) {
      return;
    }
    this.events[name].forEach((item) => {
      item.apply(this, args);
    });
  }

  /**
   * 删除
   * @param {事件队列名} args
   */
  remove(name) {
    if (this.events[name]) {
      delete this.events[name];
    }
  }
}

const bus = new EventBus();

// 不同租客订阅了不同需求房型
bus.on("bigHouse", function (value) {
  console.log(`A收到了一条消息:${value}`);
});

bus.on("mediumHouse", function (value) {
  console.log(`B收到了一条消息:${value}`);
});

bus.on("smallHouse", function (value) {
  console.log(`C收到了一条消息:${value}`);
});

// 某天房东通过中介发布了一条消息
bus.emit("bigHouse", "别墅有房了"); // A收到了一条消息:别墅有房了
// 又某一天
bus.emit("mediumHouse", "洋房有房了"); // B收到了一条消息:洋房有房了
// 再某一天。。。。
bus.emit("smallHouse", "小平层有房了"); // C收到了一条消息:小平层有房了

回顾一下,从这个例子就可可以看到:

  • EventBus这个就是相当于扮演了中介机构的角色。
  • emit发布就相当于是房东。房东不定时随机的发布消息,说某某套房子空了。
  • on订阅就相当于是房客,订阅某个户型的事件了以后就可以实时收到该户型的通知。

4、小结

  • 通过上面这些示例,两者之间最大的区别就是中介结构这一环,通过这个机构,房东和房客之间的沟通更加的顺畅了,也就是两者之间松耦合,这样的话,我们开发的时候可以将中介结构抽离成为了一个单独的文件,这样使得业务逻辑更加清晰,维护起来也更加的区别。不管这两者模式是相同还是不同的,这个我觉得是最主要的区别。
  • 其它的我这里直接引用这篇文章的小结概况一下吧,我觉得这位大佬总结还是比较到位的:
    1. 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
    2. 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
    3. 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
    4. 观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。
  • 当然,以上这些都是我自己的理解,欢迎交流。

参考学习: https://juejin.im/post/5a14e9edf265da4312808d86 https://molunerfinn.com/observer-vs-pubsub-pattern https://juejin.im/post/5bb1bb616fb9a05d2b6dccfa https://juejin.im/post/57de12355bbb50005e648bd8

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
PHP 中的几种主要加密方式原创
散列函数将数据转换成一个固定长度的字符串,这个过程是不可逆的。散列通常用于存储密码,以确保即使数据库被泄露,攻击者也无法轻易得到原始密码。
用户10819248
2024/09/29
2490
python3 rsa非对称加密与签名校验
本文由腾讯云+社区自动同步,原文地址 https://stackoverflow.club/article/python-rsa/
羽翰尘
2019/11/20
2.5K0
PHP加密解密方法及常见问题解决方案(php对称加密和非对称加密示例)
php是一种流行的服务器端编程语言,广泛用于web应用程序开发中。在实际应用中,php加密解密是非常常见的操作。本文将介绍php中常见的加密解密方法,以及常见问题的解决方案。
超级小可爱
2024/02/24
1K0
PHP 使用非对称加密算法(RSA)
  以前一直对客户端传给服务器的信息加密这一块一脸懵,如果app里面的用户登录信息被抓包拿到了,大写着 username:root,password:123456,  那不是很尴尬。
Lansonli
2021/10/09
1.9K0
PHP 加密与解密
在现代 Web 应用中,数据安全是至关重要的,尤其是处理用户的敏感信息时,如密码、个人身份信息、信用卡号等。为了保护这些敏感数据不被黑客窃取或篡改,我们常常需要使用加密技术对数据进行保护。PHP 作为一种广泛使用的服务器端脚本语言,也提供了多种加密和解密的方法,帮助开发者实现数据的安全保护。
繁依Fanyi
2025/02/06
5390
NETCore.Encrypt:最全的加解密开源库
在软件开发中,数据安全和隐私保护是至关重要的。无论是用户信息、敏感数据还是通信内容,都需要通过加密技术来确保其安全性和完整性。对于 .NET 开发者来说,NETCore.Encrypt 是一个功能强大且易于使用的开源加密解密库,它提供了丰富的加密算法和简洁的 API,能够满足大多数开发场景中的安全需求。
郑子铭
2025/03/17
1420
NETCore.Encrypt:最全的加解密开源库
Java技术专题:「入门到精通系列」深入探索常用的六种加密技术和实现
随着信息安全的日益重要,加密技术在软件开发领域中扮演着关键的角色。Java作为一门广泛应用的编程语言,提供了丰富的加密库和API,使得开发者可以轻松实现各种加密算法。本文将深入探索Java技术中常用到的六种加密技术,包括对称加密、非对称加密、哈希算法、消息摘要、数字签名和数字证书,并通过具体的实现代码帮助读者更好地理解和应用这些加密技术。
IT_陈寒
2024/01/08
3650
Java技术专题:「入门到精通系列」深入探索常用的六种加密技术和实现
PHP OpenSSL扩展 – 对称加密
PHP 在进入7.x 时代后,默认就不再附带 mcrypt 扩展,mcrypt 将被 openssl_* 一族函数所替代。所以,对于 PHPer 来说,有必要学习一下 PHP 的 OpenSSL 扩展。
全栈程序员站长
2022/07/11
2.1K0
openssl安装与使用
可以通过源码安装也可以apt-get install安装,安装openssl之前先看一下自己是否安装有openssl
全栈程序员站长
2022/07/22
4.9K0
openssl安装与使用
PHP的openssl加密扩展使用小结
枕边书
2018/01/04
1.7K0
NodeJS模块研究 - crypto
这次研究下 nodejs 的 crypto 模块,它提供了各种各样加密算法的 API。这篇文章记录了常用加密算法的种类、特点、用途和代码实现。其中涉及算法较多,应用面较广,每类算法都有自己适用的场景。为了使行文流畅,列出了本文记录的几类常用算法:
心谭博客
2020/04/21
2.4K0
Go加密算法总结
它是一种数据编码方式,虽然是可逆的,但是它的编码方式是公开的,无所谓加密。本文也对Base64编码方式做了简要介绍。
iginkgo18
2020/12/22
1.7K0
基础入门-算法逆向&散列对称非对称&JS源码逆向&AES&DES&RSA&SHA
密文-有源码直接看源码分析算法(后端必须要有源码才能彻底知道) 密文-没有源码1、猜识别 2、看前端JS(加密逻辑是不是在前端) #算法加密-概念&分类&类型
没事就要多学习
2024/07/18
1810
基础入门-算法逆向&散列对称非对称&JS源码逆向&AES&DES&RSA&SHA
加解密算法分析与应用场景
在日常开发中,无论是使用何种编程语言,我们都会遇到加解密的需求。例如,为了保护接口数据安全,我们需要对数据进行加密传输;在HTTPS协议中,通过非对称加密传输客户端私钥,然后双方使用该私钥进行对称加密通信;使用MD5算法进行文件一致性校验等。然而,面对众多的加解密方案,我们往往不清楚何时使用哪种方法。本文将为您梳理当前主流的加解密技术,并对算法进行科普性说明,但不涉及具体算法分析。根据日常应用场景,加解密技术大致可分为以下四类:
不惑
2024/05/13
6320
加解密算法分析与应用场景
了解主流加密方式:离散、对称与非对称加密算法
尊敬的读者们,大家好!今天我将为大家介绍计算机加密领域中主流的加密方式,帮助您更好地理解加密算法。我们将重点探讨离散加密、对称加密以及非对称加密算法,并以Go语言为示例进行说明。让我们一起深入了解这些关键的加密技术!
运维开发王义杰
2023/08/10
4880
了解主流加密方式:离散、对称与非对称加密算法
Bouncy Castle :国密加密在.NET Core中的实践
Bouncy Castle 是一个开源的密码学库, 提供了丰富的加密算法实现,包括常见的对称加密算法(如 AES)、非对称加密算法(如 RSA、ECC)、哈希算法(如 SHA-256)以及数字签名算法(如 ECDSA)
郑子铭
2025/03/20
1930
Bouncy Castle :国密加密在.NET Core中的实践
Golang:加密解密算法
在项目开发过程中,当操作一些用户的隐私信息,诸如密码,帐户密钥等数据时,往往需要加密后可以在网上传输.这时,需要一些高效地,简单易用的加密算法加密数据,然后把加密后的数据存入数据库或进行其他操作;当需要读取数据时,把加密后的数据取出来,再通过算法解密.
OwenZhang
2021/12/08
1.8K0
Golang:加密解密算法
PHP的OpenSSL加密扩展学习(一):对称加密
我们已经学过不少 PHP 中加密扩展相关的内容了。而今天开始,我们要学习的则是重点中的重点,那就是 OpenSSL 加密扩展的使用。为什么说它是重点中的重点呢?一是 OpenSSL 是目前 PHP 甚至是整个开发圈中的数据加密事实标准,包括 HTTPS/SSL 在内的加密都是它的实际应用,二是 OpenSSL 提供了对称和非对称加密的形式,也就是我们日常中最普遍的两种加密方式,这都是我们需要掌握的内容。
硬核项目经理
2020/11/03
2.4K0
PHP的OpenSSL加密扩展学习(一):对称加密
哈希算法是对称算法还是非对称算法_对称加密和非对称加密原理
作用:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。 哈希算法的目的:为了验证原始数据是否被篡改。 哈希算法最重要的特点就是: 相同的输入一定得到相同的输出; 不同的输入大概率得到不同的输出。
全栈程序员站长
2022/11/01
1.3K0
请看,常见的加密算法及详解都在这里!
加密算法,是现在每个软件项目里必须用到的内容。广泛应用在包括了用户登入、数字签名、数据传输等多个场合。那大家都知道那些呢?今天我把常见的加密算法全部整理在这里,供大家学习参考。
攻城狮的那点事
2019/12/10
1.5K0
推荐阅读
相关推荐
PHP 中的几种主要加密方式原创
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档