观察者模式、中介者模式和发布订阅模式

观察者模式

定义

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

观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯

例如生活中,我们可以用报纸期刊的订阅来形象的说明,当你订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸

报社和订报纸的客户就形成了一对多的依赖关系
在这里插入图片描述
被观察者知道观察者的存在,同时管理所有的观察者

实现

class Observer {update(params) {console.log(params)}
}class Demo {update(params) {console.log(params)}
}class ObserverList {constructor() {this.observerList = []}add(observer) {this.observerList.push(observer);return}delete(observer) {this.observerList = this.observerList.filter(ob => ob !== observer);return this;}get(index) {return this.observerList[index];}count() {return this.observerList.length;}
}class Subject {observers = new ObserverList;add(observer) {this.observers.add(observer)}remove(observer) {this.observers.delete(observer);}notify(...params) {for (let i = 0; i < this.observers.count(); i++) {let item = this.observers.get(i)item.update(...params)}}
}let sub = new Subject()
sub.add(new Observer)
sub.add(new Observer)
sub.add(new Demo)sub.notify('测试观察者模式发出通知')

在这里插入图片描述

中介者模式

定义

在这里插入图片描述

在这个星型结构中,同事对象不再直接与其他的同事对象联系,通过中介者对象与另一个对象发生相互作用,中介者对象的存在保证了结构上的稳定,也就是说,系统的结构不会因为新对象的引入带来大量的修改工作。

如果一个系统中对象之间存在多对多的相互关系,可以将对象之间的一些交互行为从各个对象之间分离出来,并集中封装在一个中介者对象中,由中介者进行统一的协调,这样对象之间多对多的复杂关系就转变为相对简单的一对多关系,通过引入中介者来简化对象之间的复杂交互。

实现

以租房为例,租房者和房主都通过中介更新信息,中介将更新后的信息通知对应的对象

class Tenant {constructor(name, mediator) {this.name = name;this.mediator = mediator}contract(message) {this.mediator.contract(message, this)}getMessage(message) {console.log(message)}
}class HouseOwner {constructor(name, mediator) {this.name = name;this.mediator = mediator}contract(message) {this.mediator.contract(message, this)}getMessage(message) {console.log(message)}
}class Mediator {constructor(houseOwner, tenant) {this.houseOwner = houseOwnerthis.tenant = tenant}contract(message, person) {if (person == this.houseOwner) {this.tenant.getMessage(message)} else {this.houseOwner.getMessage(message)}}getTenant() {return this.tenant}setTenant(tenant) {this.tenant = tenant}getHouseOwner() {return this.houseOwner}setHouseOwner(houseOwner) {this.houseOwner = houseOwner}
}let mediator = new Mediator()let tenant = new Tenant('tenant',mediator)
let houseOwner = new HouseOwner('houseOwner',mediator)
mediator.setTenant(tenant)
mediator.setHouseOwner(houseOwner)tenant.contract('你好房东 我是租客')
houseOwner.contract('你好租客 我是房东')

优点

  • 简化交互:中介者模式简化了对象之间的交互,它用中介者和租客房东的一对多交互代替了原来租客房东的多对多交互,一对多容易理解和扩展,将原本难以理解的网状结构转换为星型结构
  • 解耦租客房东对象:中介者模式可将各个租客房东对象解耦,有利于租客房东之间的松耦合,可以独立改变和复用每一个租客房东和中介者,增加新的中介者和新的租客房东类都很方便,更好地符合开闭原则
  • 减少租客房东子类个数:中介者将原本分布于多个对象间的行为集中起来,改变这些行为只需要生成新的中介者子类即可,这使得各个租客房东类可以被重用,无须对租客房东类进行扩展

缺点

中介者类复杂:由于具体中介者中包含了大量的同事之间的交互细节,可能会导致具体中介者类变得非常复杂,使得系统难以维护

发布订阅模式

发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在

同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在

class PubSub {constructor() {this.messages = {};this.listeners = {};}// 添加发布者publish(type, content) {const existContent = this.messages[type];if (!existContent) {this.messages[type] = [];}this.messages[type].push(content);}// 添加订阅者subscribe(type, cb) {const existListener = this.listeners[type];if (!existListener) {this.listeners[type] = [];}this.listeners[type].push(cb);}// 通知notify(type) {const messages = this.messages[type];const subscribers = this.listeners[type] || [];subscribers.forEach((cb, index) => cb(messages[index]));}
}class Publisher {constructor(name, context) {this.name = name;this.context = context;}publish(type, content) {this.context.publish(type, content);}
}class Subscriber {constructor(name, context) {this.name = name;this.context = context;}subscribe(type, cb) {this.context.subscribe(type, cb);}
}const TYPE_A = 'music';
const TYPE_B = 'movie';
const TYPE_C = 'novel';const pubsub = new PubSub();const publisherA = new Publisher('publisherA', pubsub);
publisherA.publish(TYPE_A, 'we are young');
publisherA.publish(TYPE_B, 'the silicon valley');
const publisherB = new Publisher('publisherB', pubsub);
publisherB.publish(TYPE_A, 'stronger');
const publisherC = new Publisher('publisherC', pubsub);
publisherC.publish(TYPE_C, 'a brief history of time');const subscriberA = new Subscriber('subscriberA', pubsub);
subscriberA.subscribe(TYPE_A, res => {console.log('subscriberA received', res)
});
const subscriberB = new Subscriber('subscriberB', pubsub);
subscriberB.subscribe(TYPE_C, res => {console.log('subscriberB received', res)
});
const subscriberC = new Subscriber('subscriberC', pubsub);
subscriberC.subscribe(TYPE_B, res => {console.log('subscriberC received', res)
});pubsub.notify(TYPE_A);
pubsub.notify(TYPE_B);
pubsub.notify(TYPE_C);

灵感来源于:addEventListener DOM2事件绑定

  • 给当前元素的某一个事件行为,绑定多个不同的方法「事件池机制」
  • 事件行为触发,会依次通知事件池中的方法执行
  • 支持内置事件{标准事件,例如:click、dblclick、mouseenter…}

应用场景:凡是某个阶段到达的时候,需要执行很多方法「更多时候,到底执行多少个方法不确定,需要编写业务边处理的」,我们都可以基于发布订阅设计模式来管理代码;创建事件池->发布计划 向事件池中加入方法->向计划表中订阅任务 fire->通知计划表中的任务执行

let sub = (function () {let pond = {};// 向事件池中追加指定自定义事件类型的方法const on = function on(type, func) {// 每一次增加的时候,验证当前类型在事件池中是否已经存在!Array.isArray(pond[type]) ? pond[type] = [] : null;let arr = pond[type];if (arr.includes(func)) return;arr.push(func);};// 从事件池中移除指定自定义事件类型的方法const off = function off(type, func) {let arr = pond[type],i = 0,item = null;if (!Array.isArray(arr)) throw new TypeError(`${type} 自定义事件在事件池中并不存在!`);for (; i < arr.length; i++) {item = arr[i];if (item === func) {// 移除掉// arr.splice(i, 1); //这样导致数据塌陷arr[i] = null; //这样只是让集合中当前项值变为null,但是集合的机构是不发生改变的「索引不变」;下一次执行emit的时候,遇到当前项是null,我们再去把其移除掉即可;break;}}};// 通知事件池中指定自定义事件类型的方法执行const emit = function emit(type, ...params) {let arr = pond[type],i = 0,item = null;if (!Array.isArray(arr)) throw new TypeError(`${type} 自定义事件在事件池中并不存在!`);for (; i < arr.length; i++) {item = arr[i];if (typeof item === "function") {item(...params);continue;}//不是函数的值都移除掉即可,自己控制i的值arr.splice(i, 1);i--;}};return {on,off,emit};
})();const fn1 = () => console.log(1);
const fn2 = () => console.log(2);
const fn3 = () => {console.log(3);sub.off('A', fn1);sub.off('A', fn2);
};
const fn4 = () => console.log(4);
const fn5 = () => console.log(5);
const fn6 = () => console.log(6);sub.on('A', fn1);
sub.on('A', fn2);
sub.on('A', fn3);
sub.on('A', fn4);
sub.on('A', fn5);
sub.on('A', fn6);
setTimeout(() => {sub.emit('A');
}, 1000);setTimeout(() => {sub.emit('A');
}, 2000);

观察者模式和发布订阅模式的区别

  • 观察者模式:某公司给自己员工发月饼发粽子,是由公司的行政部门发送的,这件事不适合交给第三方,原因是“公司”和“员工”是一个整体

  • 发布-订阅模式:某公司要给其他人发各种快递,因为“公司”和“其他人”是独立的,其唯一的桥梁是“快递”,所以这件事适合交给第三方快递公司解决
    上述过程中,如果公司自己去管理快递的配送,那公司就会变成一个快递公司,业务繁杂难以管理,影响公司自身的主营业务,因此使用何种模式需要考虑什么情况两者是需要耦合的

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

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

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

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

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

相关文章

Pytorch(一)

目录 一、基本操作 二、自动求导机制 三、线性回归DEMO 3.1模型的读取与保存 3.2利用GPU训练时 四、常见的Tensor形式 五、Hub模块 一、基本操作 操作代码如下: import torch import numpy as np#创建一个矩阵 x1 torch.empty(5,3)# 随机值 x2 torch.rand(5,3)# 初始化…

NoSQL之Redis配置使用

目录 一、关系数据库与非关系型数据库 1.1.关系型数据库的概述 1.2关系型数据库的优缺点 1.2.1优点 1.2.2缺点 1.3.非关系型数据库的概述 二.关系数据库与非关系型数据库的区别 2.1数据存储方式不同 2.2扩展方式不同 2.3对事务性的支持不同 2.4非关系型数据库产生背景 2…

浅析嵌入式GUI框架-LVGL

LVGL是什么&#xff1f; LVGL (Light and Versatile Graphics Library) 是最流行的免费开源嵌入式图形库&#xff0c;可为任何 MCU、MPU 和显示类型创建漂亮的 UI。 嵌入式GUI框架对比 Features/框架LVGLFlutter-elinuxArkUI(鸿蒙OS)AWTKQTMIniGUIemWinuC/GUI柿饼UI跨平台…

【Golang】Golang进阶系列教程--为什么 Go 语言 struct 要使用 tags

文章目录 前言struct tags 的使用使用反引号避免使用空格避免重复使用标准化的 tag 名称多个 tag 值 struct tags 的原理struct tags 的优势常用的 struct tags参考文章&#xff1a; 前言 在 Go 语言中&#xff0c;struct 是一种常见的数据类型&#xff0c;它可以用来表示复杂…

《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(8)-Fiddler如何设置捕获会话

1.简介 前边几篇宏哥介绍了Fiddler界面内容以及作用。今天宏哥就讲解和分享如何设置Fiddler后&#xff0c;我们就可以捕获会话&#xff0c;进行抓包了。 2.捕获会话的设备 常见的捕获会话的设备分为PC&#xff08;电脑&#xff09;端和手机&#xff08;Android和IOS苹果&…

Bert经典变体学习

ALBert ALBERT就是为了解决模型参数量大以及训练时间过长的问题。ALBERT最小的参数只有十几M, 效果要比BERT低1-2个点&#xff0c;最大的xxlarge也就200多M。可以看到在模型参数量上减少的还是非常明显的&#xff0c;但是在速度上似乎没有那么明显。最大的问题就是这种方式其实…

使用CRM分析数据有哪些功能?

CRM数据分析软件可以帮助企业增强竞争力&#xff0c;并更好地了解客户需求及市场变化&#xff0c;助力企业数据分析&#xff0c;并提供实时更新的数据和分析结果&#xff0c;CRM数据分析软件的主要特点是什么&#xff1f;包括以下6个特点。 CRM数据分析软件的主要功能通常包括…

SpringBoot 和 Vue 参数类型不对应,导致method parameter is not present

org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter id for method parameter type String is not present 客户端&#xff1a; requestUserInfoById()const requestUserInfoById async (id?: string) > {} 服务器端&am…

亚马逊云科技全新Amazon Bedrock,助力客户构建生成式AI应用

亚马逊云科技近日在纽约峰会上宣布全面扩展其全托管基础模型服务Amazon Bedrock&#xff0c;包括新增Cohere作为基础模型供应商&#xff0c;加入Anthropic和Stability AI的最新基础模型&#xff0c;并发布变革性的新功能Amazon Bedrock Agents功能。客户无需管理任何基础设施&a…

ChatGPT统计“一到点就下班”的人数

ChatGPT统计“一到点就下班”的人数 1、到点下班 Chatgpt统计各部门F-D级员工到点下班人数占比&#xff0c;是在批评公司内部存在到点下班现象。 根据图片&#xff0c;该占比的计算方法是&#xff1a;最后一次下班卡在17:30-17:40之间&#xff0c;且1-5月合计有40天以上的人…

OpenCV图像处理-视频分割静态背景-MOG/MOG2/GMG

视频分割背景 1.概念介绍2. 函数介绍MOG算法MOG2算法GMG算法 原视频获取链接 1.概念介绍 视频背景扣除原理&#xff1a;视频是一组连续的帧&#xff08;一幅幅图组成&#xff09;&#xff0c;帧与帧之间关系密切(GOP/group of picture)&#xff0c;在GOP中&#xff0c;背景几乎…

关于Java中的Lambda变量捕获

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 目录 一、Lambda表达式语法 二、Lambda中变量捕获 一、Lambda表达式语法 基本语法: (parameters) -> expression 或 (parameters) ->{ statements; } Lambda表达式由三部分组成&a…

使用Feign出现空指针异常

说明&#xff1a;本文记录一次偶然出现的空指针异常&#xff0c;在微服务架构中&#xff0c;一个服务在调用另一个服务时&#xff0c;出现了空指针异常。 业务描述&#xff1a;在做订单超时功能时&#xff0c;大家都知道&#xff0c;可以使用RabbitMQ延迟队列&#xff0c;下单…

数据结构—数组和广义表

4.2数组 数组&#xff1a;按一定格式排列起来的&#xff0c;具有相同类型的数据元素的集合。 **一维数组&#xff1a;**若线性表中的数据元素为非结果的简单元素&#xff0c;则称为一维数组。 **一维数组的逻辑结构&#xff1a;**线性结构&#xff0c;定长的线性表。 **声明…

TD1850多用表校准系统参考标准

参考标准 分类 标准名称 国家标准 GB/T 13978-2008 数字多用表 GB/T 15637-2012 数字多用表校准仪通用规范 计量法规 JJF 1075-2015 钳形电流表校准规范 JJF 1284-2011 交直流电表校验仪校准规范 JJF 1587-2016 数字多用表校准规范 JJG 124-2005 电流表、电压表、功率表及…

【多模态】18、ViLD | 通过对视觉和语言知识蒸馏来实现开集目标检测(ICLR2022)

文章目录 一、背景二、方法2.1 对新类别的定位 Localization2.2 使用 cropped regions 进行开放词汇检测2.3 ViLD 三、效果 论文&#xff1a;Open-vocabulary Object Detection via Vision and Language Knowledge Distillation 代码&#xff1a;https://github.com/tensorflo…

三子棋(超详解+完整码源)

三子棋 前言一&#xff0c;游戏规则二&#xff0c;所需文件三&#xff0c;创建菜单四&#xff0c;游戏核心内容实现1.棋盘初始化1.棋盘展示3.玩家下棋4.电脑下棋5.游戏胜负判断6.game&#xff08;&#xff09;函数内部具体实现 四&#xff0c;游戏运行实操 前言 C语言实现三子棋…

8.10 PowerBI系列之DAX函数专题-TopN中实现动态指标

需求 实现 建立一个辅助表供切片器选择 2 建立条件判断度量值top_measure swich(true(),selectedvalue(table[tope_type])"按数量top",sum(order_2[产品数量]),selectedvalue(table[tope_type])"按金额top",sum(order_2[订单金额]),selectedvalue(table…

aop实现方式及基本使用

aop实现方式 aspectj 编译器增强&#xff0c;直接修改源码可以不借助Spring实现 也没有用代理对象 &#xff08;ajc编译器&#xff09; aop 的原理并非代理一种, 编译器也能玩出花样&#xff08;直接修改源码&#xff09; 运行时需要在 VM options 里加入 -javaagent:D:/envir…

Matlab的SimuLink对FS32K144编程--内部数据存储Flash

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 前言 Flah擦写是由寿命的&#xff0c;应当减免无效的擦写&#xff0c;如数据值不变不进行擦写 1、新建工程完成后&#xff0c;拖出Flash的存储控制初始化…