【设计模式】发布订阅模式,构建可以接收历史消息的观察者类

什么是发布订阅模式?

发布订阅模式也被称为观察者模式,这个模式中有两种角色:发布者(被观察者)订阅者(观察者)

通常的操作是:订阅者订阅发布者的某一个事件,发布者接收到这个事件变化的时候,通知所有的订阅者。

举个栗子:你在微信中关注了一个公众号,这个公众号在更新文章的时候,微信会将新的文章推送到你的微信消息中,公众号是发布者,你的微信号是订阅者。

你可以关注多个公众号,公众号也可以有多个粉丝。

一个简单的观察者类

class Observer {/*** 事件队列*/static events = {};/*** 订阅* @param {*} topic* @param {*} callback*/static subscribe(topic, callback) {if (!Observer.events[topic]) {Observer.events[topic] = [];}Observer.events[topic].push(callback);return () => {const index = Observer.events[topic].findIndex((item) => item === callback);Observer.events[topic].splice(index, 1);};}/*** 发布* @param {*} topic* @param  {*} data*/static publish(topic, data) {if (Observer.events[topic]) {Observer.events[topic].forEach((fn) => {fn(data);});} else {console.error(`topic [${topic}] is empty!`);}}
}

订阅消息:

// 订阅消息
const unsubscribe = Observer.subscribe("update", (data) => {console.log(data);
});// 取消订阅
unsubscribe();

发布消息:

Observer.publish("update", 1);

可以接收历史消息的观察者类

上面的观察者无法接收历史消息,如果一个消息在未订阅时就已经发布,那么这条消息就会被漏掉。

一个可能的场景是:在一个页面中,需要在导航栏中展示用户信息,用户信息需要通过网络请求来获取,获取到后通过 Observer.publish() 方法发布,导航栏通过 Observer.subscribe() 方法来获取用户信息及回调。

这个场景中如果用户信息已经获取,但是导航栏组件还未加载,这种情况下导航栏就再也拿不到用户信息了。

解决方案就是维护一个历史消息列表,在调用 Observer.subscribe() 订阅消息时如果有历史消息则立即触发回调。

class Observer {/*** 历史消息*/static history = {};/*** 事件队列*/static events = {};/*** 订阅* @param {*} topic* @param {*} callback*/static subscribe(topic, callback) {if (!Observer.events[topic]) {Observer.events[topic] = [];}Observer.events[topic].push(callback);// 如果有历史消息if (Observer.history[topic]) {callback(Observer.history[topic]);}return () => {const index = Observer.events[topic].findIndex((item) => item === callback);Observer.events[topic].splice(index, 1);};}/*** 发布* @param {*} topic* @param  {...any} args*/static publish(topic, data) {if (Observer.events[topic]) {Observer.events[topic].forEach((fn) => {fn(data);});}if (!Observer.history[topic]) {Observer.history[topic] = [];}// 保存历史消息Observer.history[topic].push(data);}
}

可以先发布后订阅:

Observer.publish("update", 222);
Observer.publish("update", 333);const unsubscribe = Observer.subscribe("update", (data) => {if (Array.isArray(data)) {// 历史消息列表} else {// 最新消息}console.log(data);
});Observer.publish("update", 444);

总结

发布订阅模式的优点是可以很方便的实现不同模块之间的通信。

它的缺点在于,观察者对象本身是占用内存的,而且当你订阅一个消息后,也许此消息再也没有发布过,但这个观察者对象会始终存在于内存中。

发布订阅模式弱化了对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也将被深埋在背后,会导致程序难以跟踪维护和理解。

当多个发布者和订阅者嵌套到一起的时候,很难捋清楚他们之间的关系。

可以用,但别到处都用。

参考

《JavaScript 设计模式与开发实践》

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

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

相关文章

C语言例题29:在屏幕上显示一个等腰三角形

#include <stdio.h>void main() {int i, j;int x;printf("输入等腰三角形行数&#xff1a;");scanf("%d", &x);for (i 1; i < x; i) {for (j i; j < x; j) {printf(" "); //输出空格占位}for (j 1; j < 2 * i; j) {printf…

接字符串注入攻击简介

连接字符串注入攻击简介 什么是连接字符串注入攻击&#xff1f; 连接字符串注入攻击是一种安全攻击类型&#xff0c;攻击者通过修改应用程序使用的数据库连接字符串来注入恶意内容。连接字符串是包含数据库连接所需数据&#xff08;如服务器地址、数据库名、用户名和密码等&a…

5.1 海思SS928开发 - kernle开发 - 镜像制作

5.1 kernle开发 - 镜像制作 本文主要讲解如何编译生成可用的 ss928 kernel 镜像。 ATF&#xff08;ARM Trusted Firmware&#xff09;准备 准备步骤如下&#xff1a; cd ~/hiss928/kernel/ss928_kernel_v4.19/ # 拷贝相关文件 cp -rf ~/hiss928/sdk/ema_2.0.2.2/SS928V100_SD…

吴恩达2022机器学习专项课程(一) 7.1 逻辑回归的成本函数第三周课后实验:Lab4逻辑回归的损失函数

问题预览/关键词 上节课回顾逻辑回归模型使用线性回归模型的平方误差成本函数单个训练样本的损失损失函数&#xff0c;成本函数&#xff0c;代价函数的区别线性回归损失函数和逻辑回归损失函数的区别逻辑回归模型的成本函数是什么&#xff1f;逻辑回归模型的损失函数实验逻辑回…

php7文件加密方法

文件加密 php5php7的加密方法 tp3&#xff0c;使用php5, 使用的加密工具是 zend guard 操作流程&#xff1a;在window下加密源码&#xff0c;FTP上传到linux服务器&#xff0c;根据已经装好的Zend guard 扩展自动解析加密后的文件。 加密步骤&#xff1a; 3.1&#xff1a;win…

深信服超融合虚拟机备份报错显示准备备分镜像失败

问题&#xff1a;最近一段时间深信服超融合虚拟机在执行备份策略时总是报错&#xff0c;备份空间又还很富余。 解决办法&#xff1a; 1 删除备份失败虚拟机的所有备份 2 解绑该虚拟机的备份策略 可靠服务>>备份与CDP>> 找到备份策略>>点【编辑】>>…

C++中常见容器总结Array-Vector-List-Queue-Stack-Map-Set

在 C 中&#xff0c;有许多常见的容器&#xff0c;每种都有其特定的用途和性能特征。以下是一些常见的容器类型&#xff1a;1. 数组&#xff08;Array&#xff09;&#xff1a;是一组连续存储的相同类型元素的集合。数组的大小在创建时就确定&#xff0c;并且不能动态改变。2. …

【算法模版】数据结构模版

C STL简介 vector 变长数组&#xff0c;采用倍增的思想。size(): 返回元素个数。empty(): 返回是否为空。clear(): 清空容器。front()/back(): 返回第一个/最后一个元素。push_back()/pop_back(): 在尾部插入/删除一个元素。begin()/end(): 返回迭代器指向容器的起始/末尾位置…

P44,45 属性预处理,执行后游戏效果回调,附录指定区域内修改变量

这节课主要是怎么对Attribute进行在进行到游戏角色前先进行处理,以及游戏效果如何回调 AuraAttributeSet.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "AttributeSet.h&…

实验五 Spark SQL编程初级实践

Spark SQL编程初级实践 Spark SQL基本操作 将下列JSON格式数据复制到Linux系统中&#xff0c;并保存命名为employee.json。 { "id":1 , "name":" Ella" , "age":36 } { "id":2, "name":"Bob","a…

堆的介绍,实现(c语言实现)

目录 堆的概念 堆的性质&#xff1a; 堆的分类 父子结点的下标关系 堆的向下调整算法 ​编辑小堆 大堆 建堆 堆的向上调整算法 小堆 大堆 堆的基本操作 定义堆 初始化堆 销毁堆 打印堆 堆的插入 堆的删除 大堆&#xff08;Max Heap&#xff09;的向下调整算法…

CentOS系统服务器装机后常用的操作命令大全

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

Vim 编辑器中大写键的命令

Vim 编辑器中有很多大写键的命令&#xff0c;这些命令通常用于执行特定的操作或进入特定的模式。 A&#xff1a;在当前行的末尾进入插入模式。B&#xff1a;向后移动一个单词。C&#xff1a;更改从当前光标位置到行尾的内容。进入插入模式。D&#xff1a;删除从当前光标位置到…

【Linux系统编程】基础指令(三)

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

Redis底层数据结构之IntSet

目录 一、概述二、IntSet结构三、自动升级 redis底层数据结构已完结&#x1f44f;&#x1f44f;&#x1f44f;&#xff1a; ☑️redis底层数据结构之SDS☑️redis底层数据结构之ziplist☑️redis底层数据结构之quicklist☑️redis底层数据结构之Dict☑️redis底层数据结构之Int…

java中switch条件语句的用法、switch的三种语法、switch支持的参数类型

文章目录 一、switch的应用场景二、switch三种语法2.1、switch 标准方式2.2、switch - > 用法2.2、switch yield 用法 三、什么是case穿透&#xff1f;四、示例4.1、标准示例4.2、错误示例4.3、引申用法&#xff08;多条件合并&#xff09; 一、switch的应用场景 在分支结构…

Elasticsearch概念 使用docker安装Elasticsearch和kibana

目录 一、Elasticsearch概念 倒排索引和正向索引 正向和倒排 二、ES安装 三、安装 kibana 四、IK分词器 下载ES中文分词器 扩展或停用词条 一、Elasticsearch概念 倒排索引和正向索引 正向索引 就像在mysql数据中搜索非主键字段的内容&#xff0c;就需要逐条数据的去查…

WEB攻防-.NET特性常见漏洞

目录 前置知识&#xff1a; DLL文件 .NET和DLL文件 C#和DLL文件 关系总结 .NET 配置调试-信息泄露 .NET 源码反编译-DLL 反编译与未授权访问 编译DLL文件 反编译DLL文件 注意事项 案例&#xff1a; 验证代码文件有没有可以绕过&#xff08;Cookie&Session&…

【C++】二叉树的进阶

二叉树的进阶 二叉搜索树概念操作实现创建树形结构拷贝构造函数构造函数析构函数赋值运算符重载循环版本查找插入删除 递归版本查找插入删除 应用K模型KV模型性能分析 二叉树进阶面试题二叉树创建字符串二叉树的分层遍历I最近公共祖先二叉搜索树与双向链表前序遍历与中序遍历构…

toFixed() 保留小数不精准,大数据计算 bignumber.js、big.js

Big.js: Big.js 是另一个类似于 Decimal.js 的高精度计算库&#xff0c;它也可以解决 JavaScript 浮点数计算的精度问题。 npm install big.js const Big require(big.js);const a new Big(0.1); const b new Big(0.2); const result a.plus(b); // 使用 Big.js 进行加法运…