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

观察者模式

定义:

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

结构图:

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.hqwc.cn/news/499063.html

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

相关文章

ai作画在线生成!这8个AI生图工具一定要知道。

过去的2023年被称作AI元年&#xff0c;随之而来的2024&#xff0c;被业内人士称之为AI应用元年&#xff0c;即随着大模型和各类AI应用的涌现速度放缓&#xff0c;人们关注的焦点也从产品层面&#xff08;有哪些好用的AI应用&#xff09;&#xff0c;转移到AI如何更好地赋能实际…

【Godot4自学手册】第十七节主人公的攻击和敌人的受伤

本节主要学习主人公是如何向敌人发起进攻的&#xff0c;敌人是如何受伤的&#xff0c;受伤时候动画显示&#xff0c;击退效果等。其原理和上一节内容相同&#xff0c;不过有许多细节需要关注和完善。 一、修改Bug 在本节学习之前&#xff0c;我将要对上一节的代码进行完善&am…

androidframework开发面试,阿里P8成长路线

字节跳动Android面经 一面问的 Java 和 Android 基础 1、Jvm虚拟机 2、messageQueue会不会阻塞ui线程 3、对象锁和类锁 4、之字形打印树 5、还有其他的 《Android学习笔记总结最新移动架构视频大厂安卓面试真题项目实战源码讲义》 **完整开源项目&#xff1a;docs.qq.com/doc…

【leetcode】破解闯关密码 模板字符串

/*** param {number[]} password* return {string}*/ var crackPassword function(password) {return minNumspassword.sort((a,b)>{if(${a}${b}-${b}${a}>0){return 1;}else{return -1;}}).join(""); };巧用模板字符串对数组进行排序

二、TensorFlow结构分析(2)

目录 1、会话 1.1 __init__(target,graphNone,configNone) 1.2 会话的run() 1.3 feed操作 TF数据流图图与TensorBoard会话张量变量OP高级API 1、会话 1.1 __init__(target,graphNone,configNone) def session_demo():# 会话的演示# Tensorflow实现加法运算a_t tf.constan…

Vue+SpringBoot打造音乐偏好度推荐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 音乐档案模块2.1.2 我的喜好模块2.1.3 每日推荐模块2.1.4 通知公告模块 2.2 用例图设计2.3 实体类设计2.4 数据库设计 三、系统展示3.1 登录注册3.2 音乐档案模块3.3 音乐每日推荐模块3.4 通知公告模…

继承-学习2

this关键字&#xff1a;指向调用该方法的对象&#xff0c;一般我们是在当前类中使用this关键字&#xff0c;所以我们常说代表本类对象的引用 super关键字&#xff1a;代表父类存储空间的标识(可看作父类对象的引用) 父类&#xff1a; package ven;public class Fu {//父类成员…

【云原生】Spring Cloud Gateway的底层原理与实践方法探究

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &#x1f680; 本…

Win32汇编ListView控件学习

此控件比较复杂&#xff1b;和基础win32控件不同&#xff1b;需要先初始化Windows公共控件库&#xff0c; invoke InitCommonControls 之后才可使用&#xff1b; lvdemo.asm&#xff0c; .386.model flat, stdcalloption casemap :none ; case sensitiveinclude window…

Windows Docker 部署 SQL Server

部署 SQL Server 打开 Docker Desktop&#xff0c;切换到 Linux 内核。然后在 PowerShell 执行下面命令&#xff0c;即可启动一个 SQL Server 服务&#xff0c;这里安装的是 2022 年版本 docker run -e "ACCEPT_EULAY" -e "MSSQL_SA_PASSWORDSQL123abcABC!&qu…

VR全景HDR拍摄教程

什么是HDR&#xff1f; HDR可以用在哪里&#xff1f; 书面解释&#xff1a;HDR&#xff08;高动态范围 High Dynamic Range&#xff09;摄影&#xff0c;是摄影领域广泛使用的一种技术。 是不是有点懵&#xff1f; 我们来看一个实际的拍摄现场环境&#xff0c;你就懂了 我们…

电商网站数据采集配合socks5代理ip怎么进行?

电商网站数据采集是一项重要的任务&#xff0c;可以帮助企业了解市场需求、竞品分析、用户行为等方面。在进行电商网站数据采集时&#xff0c;有时需要配合使用socks5代理IP。本文将介绍如何进行电商网站数据采集配合socks5代理IP。 一、代理IP介绍 代理IP是一种可以隐藏用户真…