行为型:发布订阅模式

news2024/11/16 18:05:14

定义  

  发布订阅模式是基于一个事件(主题)通道,希望接收通知的对象Subscriber(订阅者)通过自定义事件订阅主题,被激活事件的对象 Publisher (发布者)通过发布主题事件的方式通知订阅者 Subscriber (订阅者)对象。

  简单说就是发布者与订阅者通过事件来通信,这里的发布者是之前观察者模式中的被观察者,订阅者是观察者模式中的观察者,他们角色定位是等价的,只不过是不同的叫法。

发布订阅与观察者模式

  平时我们在微博中关注某个大v,这个大v 并不关心我这个订阅者具备什么特征,我只是通过微博这个平台关注了他,他也只是把他要分享的话题通过微博发出来,我和他之间并不存在直接的联系,然后我自动就能看到这个大v发布的消息,这就是发布订阅模式。

  发布订阅者模式与观察者模式类似,但是两者并不完全相同,发布订阅者模式与观察者相比多了一个中间层事件调度中心,用来对发布者发布的信息进行处理,再通知到各个特定的订阅者,大致过程如下图所示

发布者只是发布某事件,无需知道哪些订阅者,订阅者只需要订阅自己感兴趣的事件,无需关注发布者。

发布者完全不用感知订阅者,不用关心它怎么实现回调方法,事件的注册和触发都发生在独立于双方的第三方平台(调度中心)上,发布-订阅模式下,实现了完全地解耦。

通过之前对观察者模式的实现,我们的Subject类中是持有observer对象的,因此并没有实现两个类的完全解耦。通过添加中间层的调度中心类,我么可以将订阅者和发布者完全解耦,两者不再有直接的关联,而是通过调度中心关联起来。下面我们实现一个发布订阅者模式。

传统写法模拟发布订阅模式

按照上面思路,我们需要写下如下三个类,然后事件中心对象是发布者、订阅者之间的桥梁,我们很快写下如下代码:

  1. 发布者 ---- 观察者模式中的【被观察者】
  2. 订阅者 ---- 观察者模式中的【订阅者】
  3. 事件中心 ---- 类似公共的一个平台
 
/*
发布者:发布、注册xxx事件 or 主题
订阅者:自己的行为,取消订阅,订阅
事件中心:注册发布者的某事件、取消注册发布者的某事件、注册订阅者、取消订阅者、发布事件(通知订阅者)
*/
// 发布者
class Pulisher {
constructor (name, evtCenter) {
this.name = name;
this.evtCenter = evtCenter;
}
// 向事件调度中心-注册某事件
register (evtName) {
this.evtCenter.registerEvt(evtName)
}
unregister (evtName) {
this.evtCenter.unRegisterEvt(evtName)
}
// 向事件调度中心-发布某事件
publish (evtName, ...params) {
this.evtCenter.publish(evtName, ...params)
}
}
// 订阅者
class Subscriber {
constructor (name,evtCenter) {
this.name = name;
this.evtCenter = evtCenter;
}
//订阅
subscribe(evtName) {
this.evtCenter.addSubscribe(evtName, this);
}
//取消订阅
unSubscribe(evtName) {
this.evtCenter.unAddSubscribe(evtName, this);
}
//接收
update(params) {
console.log(`我接收到了,${params}`);
}
}
// 事件调度中心
class EvtCenter {
constructor (name) {
this.name = name;
this.evtHandle = {}
}
// 注册发布者要发布的事件
registerEvt (evtName) {
if (!this.evtHandle[evtName]) {
this.evtHandle[evtName] = []
}
}
// 取消注册发布者要发布的事件
unRegisterEvt (evtName) {
delete this.evtHandle[evtName];
}
// 增加订阅者-注册观察者
addSubscribe(evtName, sub) {
if (this.evtHandle[evtName]) {
this.evtHandle[evtName].push(sub);
}
}
// 取消订阅者-移除注册观察者
unAddSubscribe(evtName, sub) {
this.evtHandle[evtName].forEach((item, index) => {
if (item === sub) {
this.evtHandle[evtName].splice(index, 1);
}
});
}
// 事件调度中心-发布某事件
publish (evtName, ...params) {
this.evtHandle[evtName] && this.evtHandle[evtName].forEach((item) => {
item.update(...params);
});
}
}
// 测试
const evtCenter1 = new EvtCenter('报社调度中心1')
const pulisher1 = new Pulisher('报社1', evtCenter1)
const sub1 = new Subscriber('我是sub1, 我对日报感兴趣', evtCenter1)
const sub2 = new Subscriber('我是sub2, 我对日报感兴趣', evtCenter1)
const sub3 = new Subscriber('我是sub3, 我对中报感兴趣', evtCenter1)
const sub4 = new Subscriber('我是sub4, 我对晚报感兴趣', evtCenter1)
// 发布者-注册三个事件到事件中心
pulisher1.register('广州日报')
pulisher1.register('广州中报')
pulisher1.register('广州晚报')
// 订阅者可以自己订阅,当然也可以直接操作事件中心
sub1.subscribe('广州日报')
sub2.subscribe('广州日报')
sub3.subscribe('广州中报')
sub4.subscribe('广州晚报')
// 现在开始发布事件
pulisher1.publish('广州日报', '广州日报')
pulisher1.publish('广州中报', '广州中报')
pulisher1.publish('广州晚报', '广州晚报')
pulisher1.unregister('广州日报')
// 再一次发布事件
console.log('再一次发布事件,这次我取消了日报') // 没有输出广州日报
pulisher1.publish('广州日报', '广州日报')
pulisher1.publish('广州中报', '广州中报')
pulisher1.publish('广州晚报', '广州晚报')

简单写法--面向事件调度中心编程

在js中函数是第一等公民,天生适合回调函数,所以可以直接面向事件调度中心编码即可。我们要做的事情其实就是触发什么事件,执行什么动作。

 
// 事件调度中心
class PubSub {
constructor () {
this.evtHandles = {}
}
// 订阅
subscribe (evtName, callback) {
if (!this.evtHandles[evtName]) {
this.evtHandles[evtName] = [callback];
}
this.evtHandles[evtName].push(callback);
}
// 发布
publish(evtName, ...arg) {
if (this.evtHandles[evtName]) {
for(let fn of this.evtHandles[evtName]) {
fn.call(this, ...arg);
}
}
}
unSubscribe (evtName, fn) { // 取消订阅
let fnList = this.evtHandles[evtName];
if (!fnList) return false;
if (!fn) {
// 不传入指定取消的订阅方法,则清空所有key下的订阅
this.evtHandles[evtName] = []
} else {
fnList.forEach((item, index) => {
if (item === fn) {
fnList.splice(index, 1);
}
})
}
}
}
// 先订阅在发布
const pub1 = new PubSub()
// 订阅三个事件
pub1.subscribe('onWork', time => {
console.log(`上班了:${time}`);
})
pub1.subscribe('onWork', time => {
console.log(`上班了:${time},开始打开待办事项`);
})
pub1.subscribe('onOffWork', time => {
console.log(`下班了:${time}`);
})
pub1.subscribe('onLaunch', time => {
console.log(`吃饭了:${time}`);
})
// 发布对应的事件
pub1.publish('onWork', '09:00:00');
pub1.publish('onLaunch', '12:00:00');
pub1.publish('onOffWork', '18:00:00');
// 取消onWork 事件
pub1.unSubscribe('onWork');
// 取消订阅
pub1.unSubscribe('onWork');
console.log(`取消 onWork`);
pub1.publish('onWork', '09:00:00'); // 不会执行

小结

  1. 发布者不直接触及到订阅者、而是由统一的第三方来完成实际的通信的操作,叫做发布订阅模式
  2. 发布者(被观察者)直接操作订阅者的操作,叫做观察者模式
  3. 发布订阅模式,发布者完全不用感知订阅者,不用关心它怎么实现回调方法,事件的注册和触发都发生在独立于双方的第三方平台(事件调度中心)上,发布-订阅模式下,实现了完全地解耦。
  4. 发布订阅核心通过事件来通信,在调度中心中派发给具体的订阅者。

 

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

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

相关文章

STM32F103利用CubeMX配置开启定时中断

1、外部晶振8MHz,下载方式SWD模式,需求配置定时器1,产生每100ms一次中断 新建工程、配置晶振、选择下载方式等略 2、查阅资料,STM32F103的时钟树分配 3、配置CubeMX的时钟树 4、配置定时器-开启定时中断 5、配置定时时间 &…

VoxPoser:使用大语言模型(GPT-4)来对机器人操作的可组合三维值图【论文解读】

这是最近斯坦福的李飞飞团队的一篇论文:VoxPoser: Composable 3D Value Maps for Robotic Manipulation with Language Models 主要是通过大语言模型LLM和视觉语言模型VLM结合,来对机器人做各种日常操作,我们可以先来看下实际效果:大语言模型…

使用LangChain构建问答聊天机器人案例实战(一)

使用LangChain构建问答聊天机器人案例实战 现场演示GPT-4代码生成 本节我们会通过一个综合案例,跟大家讲解LangChain,这个案例产生的代码会直接在浏览器中运行,并且会输出结果,如图14-1所示,用户问:“What was the highest close price of IBM?”(“IBM的最高收盘价是…

【Linux命令200例】mdel删除指定目录下的多个文件

🏆作者简介,黑夜开发者,全栈领域新星创作者✌,2023年6月csdn上海赛道top4。 🏆本文已收录于专栏:Linux命令大全。 🏆本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

基于注解手写Spring的IOC(上)

一、思路 先要从当前类出发找到对应包下的所有类文件,再从这些类中筛选出类上有MyComponent注解的类;把它们都装入Map中,同时类属性完成MyValue的赋值操作。 二、具体实现 测试类结构: 测试类:myse、mycontor、BigSt…

hcq1-1300-d

禾川的产品:版本V3.22 网口1: IPV4:192.168.188.100 子网掩码:255.255.255.0 网口2: IPV4:192.168.88.100 子网掩码:255.255.255.0 功能按键: 旋转拨码0 切换 SYS\IN\OUT 指示灯及…

NLP From Scratch: 基于注意力机制的 seq2seq 神经网络翻译

NLP From Scratch: 基于注意力机制的 seq2seq 神经网络翻译 这是关于“从头开始进行 NLP”的第三篇也是最后一篇教程,我们在其中编写自己的类和函数来预处理数据以完成 NLP 建模任务。 我们希望在完成本教程后,您将继续学习紧接着本教程的三本教程&…

C#,数值计算——对数正态分布(logarithmic normal distribution)的计算方法与源程序

对数正态分布(logarithmic normal distribution)是指一个随机变量的对数服从正态分布,则该随机变量服从对数正态分布。对数正态分布从短期来看,与正态分布非常接近。但长期来看,对数正态分布向上分布的数值更多一些。 …

【机器学习】Gradient Descent

Gradient Descent for Linear Regression 1、梯度下降2、梯度下降算法的实现(1) 计算梯度(2) 梯度下降(3) 梯度下降的cost与迭代次数(4) 预测 3、绘图4、学习率 首先导入所需的库: import math, copy import numpy as np import matplotlib.pyplot as plt plt.styl…

Pytest学习教程_装饰器(二)

前言 pytest装饰器是在使用 pytest 测试框架时用于扩展测试功能的特殊注解或修饰符。使用装饰器可以为测试函数提供额外的功能或行为。   以下是 pytest 装饰器的一些常见用法和用途: 装饰器作用pytest.fixture用于定义测试用例的前置条件和后置操作。可以创建可重…

读发布!设计与部署稳定的分布式系统(第2版)笔记26_安全性上

1. 安全问题 1.1. 系统违规并不总是涉及数据获取,有时会出现植入假数据,例如假身份或假运输文件 1.2. 必须在整个开发过程中持续地把安全内建到系统里,而不是把安全像胡椒面那样在出锅前才撒到系统上 2. OWASP 2.1. Open Web Application…

DataStructure--Basic

程序设计数据结构算法 只谈数据结构不谈算法就跟去话剧院看梁山伯与祝英台结果只有梁山伯在演,祝英台生病了没来一样。 本文的所有内容都出自《大话数据结构》这本书中的代码实现部分,建议看书,书中比我本文写的全。 数据结构,直…

论文笔记——Influence Maximization in Undirected Networks

Influence Maximization in Undirected Networks ContributionMotivationPreliminariesNotations Main resultsReduction to Balanced Optimal InstancesProving Theorem 3.1 for Balanced Optimal Instances Contribution 好久没发paper笔记了,这篇比较偏理论&…

【笔试强训选择题】Day32.习题(错题)解析

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!! 文章目录 前言 一、Da…

线性代数(应用篇):第五章:特征值与特征向量、第六章:二次型

文章目录 第5章 特征值与特征向量、相似矩阵(一) 特征值与特征向量1.定义2.性质3.求解(1)具体型矩阵试根法、多项式带余除法:三阶多项式分解因式 (2)抽象型矩阵 (二) 相似1.矩阵相似(1)定义(2)性质 2.相似对角化(1)定义(2)相似对角化的条件(n阶矩阵A可相…

自动化运维工具——Ansible

自动化运维工具——Ansible 一、Ansible概述二、ansible 环境安装部署1.管理端安装 ansible2.ansible 目录结构3.配置主机清单4.配置密钥对验证 三、ansible 命令行模块1.command 模块2.shell 模块3.cron 模块4.user 模块5.group 模块6.copy 模块7.file 模块8.hostname 模块9&a…

LeetCode102.Binary-Tree-Level-Order-Traversal<二叉树的层序遍历>

题目: 思路: 写过N叉树的层序遍历,(8条消息) LeetCode429.N-Ary-Tree-Level-Order-Traversal<N 叉树的层序遍历>_Eminste的博客-CSDN博客 使用栈保存每一层的结点。然后每次当前层结束。将这一层的值添加进去res中。…

【Jetpack 之 Lifecycle】

Jetpack 之 Lifecycle 在本系列文章中,我们准备分析Jetpack 架构组件。首先我们从最基础的组件开始: Lifecycle, 可以说Jetpack 大部分架构组件都是基于Lifecycle 建立的,此也为Jetpack 架构组件的基础。 关于此此组件的介绍和简…

回答网友 修改一个exe

网友说:他有个很多年前的没有源码的exe,在win10上没法用,让俺看一下。 俺看了一下,发现是窗体设计的背景色的问题。这个程序的背景色用的是clInactiveCaptionText。clInactiveCaptionText 在win10之前的系统上是灰色,但…

【Ajax】笔记-原生jsonp跨域请求案例

原生jsonp跨域请求 输入框&#xff1a;输入后&#xff0c;鼠标移开向服务端发送请求&#xff0c;返回用户不存在(直接返回不存在&#xff0c;不做判断) JS <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><me…