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

观察者模式

定义:

观察者模式是一种行为型设计模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

结构图:

https://zh.wikipedia.org/zh-cn/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F

ES6简易代码实现: 

//ts环境下的es6类模拟,已去掉ts下的类型检查和定义
/*** 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。*/
class Observe {constructor(name) {this.name = name;}update(payload) {console.log(`${this.name}观察的数据发生了变化:${payload}`);}
}
class Subject {constructor() {this.observeList = [];}addObserve(observe) {this.observeList.push(observe);}removeObserve(observe) {this.observeList = this.observeList.filter((item) => item != observe);}notify(payload) {this.observeList.forEach((item) => item.update(payload));}
}

ES简易代码实现: 

var Observe = /** @class */ (function () {function Observe(name) {this.name = name;}Observe.prototype.update = function (payload) {console.log("".concat(this.name, "\u89C2\u5BDF\u7684\u6570\u636E\u53D1\u751F\u4E86\u53D8\u5316\uFF1A").concat(payload));};return Observe;
}());
var Subject = /** @class */ (function () {function Subject() {this.observeList = [];}Subject.prototype.addObserve = function (observe) {this.observeList.push(observe);};Subject.prototype.removeObserve = function (observe) {this.observeList = this.observeList.filter(function (item) { return item != observe; });};Subject.prototype.notify = function (payload) {this.observeList.forEach(function (item) { return item.update(payload); });};return Subject;
}());
var subject = new Subject();
var observe = new Observe("xx");
var observe1 = new Observe("yy");
subject.addObserve(observe);
subject.addObserve(observe1);
subject.notify("zzz");

输出:

观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。 

发布订阅模式

定义:

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。与观察者模式的定义相似,其最大的特点是:发布方与订阅方互不相知,双方都发送自己的消息内容到第三方,由第三方来处理。

订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。

结构图:

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

  • 在观察者模式中,观察者是知道 Subject 的,Subject 一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

  • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

  • 观察者模式大多数时候是同步的,比如当事件触发,Subject 就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。

  • 观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

图示:

ES6简易代码实现

//ts环境下的es6类模拟,已去掉ts下的类型检查和定义
class EventEmitter {constructor() {//事件列表对象this.eventList = {};}//订阅on(eventName, fn) {//如果对象eventName不存在,先创建一个空数组if (!this.eventList[eventName]) {this.eventList[eventName] = [];}//如果事件存在,把fn添加到对应eventList[eventName]列表里面this.eventList[eventName].push(fn);}//取消订阅off(eventName, fn) {let callbacks = this.eventList[eventName];if (!callbacks) return false;if (!fn) return callbacks && (callbacks.length = 0);//遍历this.eventList[eventName]事件for (let i = 0; i < callbacks.length; i++) {console.log(callbacks[i].fn, "off------>callbacks[i].fn");console.log(callbacks[i], "off-callbacks[i]");//判断那个事件==fn,相同则删除if (callbacks[i] == fn || callbacks[i].fn == fn) {callbacks.splice(i, 1);break;}}}//监听一次once(eventName, fn) {// this箭头函数表达式是正则函数表达式的语法紧凑替代方案// 它没有自己的this、arguments、super或new.target关键字绑定。let on = (...args) => {this.off(eventName, on);//args代替argumentsfn.apply(this, args);};//存储fn,确保单独使用off删除传入的fn时可以被删除掉on.fn = fn;console.log(on.fn, "once--->on.fn");console.log(on, "once-on");this.on(eventName, on);}//发布emit(eventName, data) {const callbacks = this.eventList[eventName];if (!callbacks) return;callbacks.forEach((element) => {element(data);});}
}let events = new EventEmitter();
function test(data) {console.log("test is on:" + data);
}
function test1(data) {console.log("test1:" + data);
}
function test2(data) {console.log("test2:" + data);
}

源码once方法里面的on.fn =fn的作用

once订阅注册的订阅事件名均为on

考虑以下场景:还未发布时订阅者需要取消订阅某个once事件,由于once注册事件函数名均为on,off中cb是无法判断的,因此需要添加on.fn用来标识区分,所以才有off中判断条件为cb||cb.fn

上面的解释不仅说明了once方法里面 on.fn =fn的作用也说明了off方法里面for循环中if判断的后半句callbacks[i].fn == fn。

下面从代码打印中,我们来看没有经过发布,订阅者就取消订阅的once事件

输出语句如下:

events.once("example", test2);
events.off("example", test2);

输出内容:

ES5简易代码实现

//tsc编译js文件
var EventEmitter = /** @class */ (function () {function EventEmitter() {//事件列表对象this.eventList = {};}//订阅EventEmitter.prototype.on = function (eventName, fn) {//如果对象eventName不存在,先创建一个空数组if (!this.eventList[eventName]) {this.eventList[eventName] = [];}//如果事件存在,把fn添加到对应eventList[eventName]列表里面this.eventList[eventName].push(fn);};//取消订阅EventEmitter.prototype.off = function (eventName, fn) {var callbacks = this.eventList[eventName];if (!callbacks) return false;if (!fn) return callbacks && (callbacks.length = 0);//遍历this.eventList[eventName]事件for (var i = 0; i < callbacks.length; i++) {//判断那个事件==fn,相同则删除if (callbacks[i] == fn || callbacks[i].fn == fn) {callbacks.splice(i, 1);break;}}};//监听一次EventEmitter.prototype.once = function (eventName, fn) {var _this = this;var on = function () {var args = [];for (var _i = 0; _i < arguments.length; _i++) {args[_i] = arguments[_i];}_this.off(eventName, on);//args代替argumentsfn.apply(_this, args);};//存储fn,确保单独使用off删除传入的fn时可以被删除掉on.fn = fn;this.on(eventName, on);};// 发布EventEmitter.prototype.emit = function (eventName, data) {var callbacks = this.eventList[eventName];if (!callbacks) return;callbacks.forEach(function (element) {element(data);});};return EventEmitter;
})();
var events = new EventEmitter();
function test(data) {console.log("test is on:" + data);
}
function test1(data) {console.log("test1:" + data);
}
function test2(data) {console.log("test2:" + data);
}
//1
events.on("example", test);
events.emit("example", "dddddd");
//2
events.once("example", test1);
events.emit("example", "true");
//3
events.once("example", test2);
events.emit("example", 123);
//events.off("example", test2);

输出:

参考

JavaScript 发布-订阅模式 - 掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/705912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx——安装和反向代理

Nginx安装与应用 1.1 Nginx介绍 Nginx 是一个高性能的HTTP和反向代理服务器,特点是占有内存少&#xff0c;并发能力强 Nginx可以作为静态页面的web服务器&#xff0c;同时还支持CGI协议的动态语言&#xff0c;比如perl、php等。但是不支持java。Java程序只能通过与tomcat配合…

谷歌AI发展史:从阿尔法围棋到Gemini与Gemma的开源创新

谷歌一直是人工智能领域的重要推动者。本文将回顾谷歌AI的发展历程&#xff0c;从阿尔法围棋到现如今的Gemini和Gemma&#xff0c;探讨谷歌在人工智能领域的重大突破和创新。 1. 引言 在计算机科学领域&#xff0c;谷歌一直是人工智能&#xff08;AI&#xff0…

MasterAlign全景视觉点胶应用软件说明书

MasterAlign视觉软件通过高精度的图像处理和机器学习算法&#xff0c;实现了对点胶过程的全面控制和管理。以下是关于MasterAlign在全景视觉点胶应用场景中如何使用的详细说明。看完全文相信一定能让您快速上手使用。

多维时序 | Matlab实现CPO-BiTCN-BiGRU冠豪猪优化时间卷积神经网络双向门控循环单元多变量时间序列预测模型

多维时序 | Matlab实现CPO-BiTCN-BiGRU冠豪猪优化时间卷积神经网络双向门控循环单元多变量时间序列预测模型 目录 多维时序 | Matlab实现CPO-BiTCN-BiGRU冠豪猪优化时间卷积神经网络双向门控循环单元多变量时间序列预测模型预测效果基本介绍程序设计参考资料 预测效果 基本介绍…

基于R语言的Meta分析【全流程、不确定性分析】方法与Meta机器学习技术应用

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

设计模式(六)代理模式

相关文章设计模式系列 1.代理模式简介 代理模式介绍 代理模式也叫委托模式&#xff0c;是结构型设计模式的一种。在现实生活中我们用到类似代理模式的场景有很多&#xff0c;比如代购、代理上网、打官司等。 定义 为其他对象提供一种代理以控制这个对象的访问。 代理模式…

Layer1 明星项目 Partisia Blockchain 何以打造互操作、可创新的数字经济网络

我们的目标是创建一个以用户为中心的全新数字经济网络&#xff1a;在去信任化和公平透明的环境下&#xff0c;所有的隐私数据都能够得到天然保障&#xff0c;企业、用户等各角色的协作与共享将会更顺利地进行。 —— Partisia Blockchain 团队 作为一个以 Web3 安全为技术方向的…

Linux学习之vi/vim详细介绍

目录 ​编辑 1. 什么是 vim&#xff1f; 2. vi/vim 的使用 2.1 命令模式 2.2 输入模式 2.3 底线命令模式 3. vi/vim 使用实例 3.1 使用 vi/vim 进入一般模式 3.2 按下 i 进入输入模式(也称为编辑模式)&#xff0c;开始编辑文字 3.3 按下 ESC 按钮回到一般模式…

jenkins+kubernetes+git+dockerhub构建devops云平台

Devops简介 k8s助力Devops在企业落地实践 传统方式部署项目为什么发布慢&#xff0c;效率低&#xff1f; 上线一个功能&#xff0c;有多少时间被浪费了&#xff1f; 如何解决发布慢&#xff0c;效率低的问题呢&#xff1f; 什么是Devops&#xff1f; 敏捷开发 提高开发效率&…

CKA认证,开启您的云原生之旅!

在当今数字化时代&#xff0c;云计算已经成为企业和个人发展的关键技术。而获得CKA&#xff08;Certified Kubernetes Administrator&#xff09;认证&#xff0c;将是您在云原生领域迈出的重要一步。 CKA认证是由Kubernetes官方推出的权威认证&#xff0c;它旨在验证您在Kuber…

vue:实现顶部消息横向滚动通知

前言 系统顶部展示一个横向滚动的消息通知&#xff0c;就是消息内容从右往左一直滚动。 效果如下&#xff1a; 代码 使用 <template><div class"notic-bar"><img :src"notic" class"notice-img" /><div class"noti…

C++之类和对象(2)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载 5.1 运算符重载 5.2 赋值运算符重载 2. 赋值运算符只能重载成类的成员函数不能重载成全局函数 3. 用户没有显式实现时&…

【Flink CDC(一)】实现mysql整表与增量读取

文章目录 一. 运行前准备1. 依赖1.1. Maven dependency1.2. SQL Client JAR&#xff08;推荐&#xff09; 2. 配置 MySQL 服务器&#xff08;必须&#xff09; 二. 功能说明1. 启动模式2. 全量阶段支持 checkpoint3. 关于无主键表Exactly-Once 处理 三. 实战1. 实现mysql整表与…

如何用生成式AI创建食谱,解决五岁孩童挑食问题?

如何处理孩子挑食问题&#xff0c;对父母来说可能是一个挑战。这需要耐心、创造力和策略的结合。在深入具体策略之前&#xff0c;了解五岁儿童的口味偏好仍在发展中&#xff0c;他们的饮食行为受多种因素影响&#xff0c;包括气质、接触不同类型食物的程度以及父母对饮食的态度…

【ArcGIS】利用DEM进行水文分析:流向/流量等

利用DEM进行水文分析 ArcGIS实例参考 水文分析通过建立地表水文模型&#xff0c;研究与地表水流相关的各种自然现象&#xff0c;在城市和区域规划、农业及森林、交通道路等许多领域具有广泛的应用。 ArcGIS实例 某流域30m分辨率DEM如下&#xff1a; &#xff08;1&#xff09…

微服务学习

一、服务注册发现 服务注册就是维护一个登记簿&#xff0c;它管理系统内所有的服务地址。当新的服务启动后&#xff0c;它会向登记簿交待自己的地址信息。服务的依赖方直接向登记簿要Service Provider地址就行了。当下用于服务注册的工具非常多ZooKeeper&#xff0c;Consul&am…

【深入理解设计模式】装饰者设计模式

装饰者设计模式 装饰者设计模式&#xff08;Decorator Design Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许向现有对象添加新功能而不改变其结构。这种模式通常用于需要动态地为对象添加功能或行为的情况&#xff0c;而且这些功能可以独立于对象本身来进行扩展…

Selenium IDE插件录制网页,解放双手

1、 国内下载地址 https://www.crx4chrome.com/crx/77585/ &#xff0c;这个网络正常基本可以下载&#xff0c;目前最新版本是3.17.2。 点击Crx4Chrome下载。下载后的文件名称是&#xff1a;mooikfkahbdckldjjndioackbalphokd-3.17.2-Crx4Chrome.com.crx。 2、 安装 直接打开…

探索创造无限可能——Autodesk AutoCAD 2022(CAD 2022)系统要求

随着科技的不断进步和发展&#xff0c;计算机辅助设计&#xff08;CAD&#xff09;已经成为现代设计行业中不可或缺的一部分。在众多CAD软件中&#xff0c;Autodesk AutoCAD 2022&#xff08;CAD 2022&#xff09;无疑是最受欢迎和广泛应用的一款软件。作为一款全球领先的CAD软…

sql 行列互换

在SQL中进行行列互换可以使用PIVOT函数。下面是一个示例查询及其对应的结果&#xff1a; 创建测试表格 CREATE TABLE test_table (id INT PRIMARY KEY,name VARCHAR(50),category VARCHAR(50) );向测试表格插入数据 INSERT INTO test_table VALUES (1, A, Category A); INSE…