【设计模式-行为型】观察者模式
一、什么是观察者模式
说起观察者模式,不得不说一位观察者模式的高级应用者,朱元璋。不知道大家有没有看过胡军演的电视剧《朱元璋》。这部剧背景是元朝末年,天下大乱,朱元璋自幼父母双亡,沦为乞丐,后遁入空门,最终加入义军,南征北战,一步步登上历史舞台。剧中对朱元璋的刻画非常细腻,展现了他从底层一步步走向权力巅峰的过程。
在剧中,朱元璋为了巩固皇权,设立了锦衣卫。这一情节反映了朱元璋对权力的绝对掌控和对潜在威胁的高度警惕。其中有一个电影情结,让我记忆深刻:
监视闲赋在家的刘伯温 -------情节还原
-
刘伯温罢官回乡:朱元璋在经历了杨宪事件后变得更加多疑,开始怀疑刘伯温及其他官员。为了加强对官员的监控,朱元璋密令二虎组建了一支秘密队伍,命名为“锦衣卫”,专门监视所有皇孙臣子。刘伯温因与朱元璋政见不合,被朱元璋拒见并逐渐被孤立。刘伯温意识到朱元璋可能随时会治他的罪,于是提前写好了遗嘱。
-
归乡途中被监视:刘伯温在归乡途中被锦衣卫检校吴风半路拦截,奉命护送他回青田老家。刘伯温这才断定朱元璋可能要对他不利。在护送过程中,吴风等人虽然表面上照顾刘伯温父子的起居,但实际上一直在监视他们的行动和对话。刘伯温等待着吴风的诛杀,但直到抵达青田老家,吴风也没有动手,这让刘伯温感到非常诧异。
-
后续发展:朱元璋不断赏赐刘伯温,试图通过这种方式让刘伯温回京。刘伯温最终决定回京,但吴风再次出现,奉命护送他返回京城。
这一情节生动地体现了观察者模式的核心逻辑:通过“观察者”(锦衣卫)监视“被观察对象”(刘伯温),并将情报汇报给“主题”(朱元璋)。这种模式不仅巩固了朱元璋的皇权,还通过动态监控和及时反应,确保了明朝初年的政治稳定。基于上面的例子,我们来解释一下什么是观察者模式:观察者模式是一种行为型设计模式,它通过定义对象之间的依赖关系,使得当一个对象(主题)的状态发生变化时,所有依赖于它的对象(观察者)都会自动得到通知并更新。这种模式非常适合用于“一对多”的依赖关系,其中一个对象的状态变化需要通知多个其他对象。
二、为什么用观察者模式
通过这个例子来说明一下为什么要使用观察者模式呢(朱元璋为啥使用锦衣卫)
-
解耦合:观察者模式使得主题和被观察对象之间松耦合,主题不需要直接与被观察对象互动,而是通过观察者获取信息。朱元璋(主题)不需要直接与刘伯温(被观察对象)互动,而是通过锦衣卫(观察者)来获取信息。这种间接的监控方式使得朱元璋和刘伯温之间保持了松耦合关系。朱元璋不需要了解刘伯温的具体行动细节,只需要通过锦衣卫获取关键信息,从而减少了直接干预带来的风险。
-
动态监控:观察者模式支持动态监控,主题可以实时获取被观察对象的状态变化,并及时做出反应。朱元璋需要实时掌握刘伯温的动态,以便在必要时采取行动。通过锦衣卫的监视,朱元璋可以在刘伯温有任何异常行为时迅速做出反应,确保皇权的稳固。
-
集中管理:观察者模式通过统一的接口管理多个观察者,使得主题能够集中管理所有观察者的行为。这提高了系统的整体协调性和一致性。朱元璋通过锦衣卫统一管理对刘伯温的监视,确保所有信息都能集中汇报到他这里。这种集中管理的方式使得朱元璋能够全面掌握局势,避免信息碎片化,从而更好地做出决策。
-
扩展性:观察者模式允许动态地添加或删除观察者,而不需要修改主题的代码。这使得系统在运行时可以根据需要灵活调整监控范围和方式。朱元璋还可以添加监控对象胡惟庸,后续剧情。
-
广播通信:观察者模式支持一对多的广播通信机制,当主题的状态发生变化时,所有观察者都会收到通知。这使得系统能够高效地传递信息,减少重复劳动。指令统一由朱元璋下达,广播给锦衣卫。
三、观察者模式示例
3.1 锦衣卫Demo
下面让我们来用代码还原一下场景:
- 定义锦衣卫行为(接收任务,反馈监听信息)
public interface Observer {void receiveTask(String taskDescription); // 接收监听任务void reportBack(String report); // 向朱元璋反馈信息 } - 定义主题类朱元璋的行为
import java.util.List;public interface Subject {void assignTask(String taskDescription); // 下达监听任务void receiveReport(String report); // 接收反馈 } - 定义具体主题类朱元璋
import java.util.ArrayList; import java.util.List;public class ZhuYuanZhang implements Subject {private List<Observer> observers = new ArrayList<>();@Overridepublic void assignTask(String taskDescription) {System.out.println("朱元璋下达监听任务:" + taskDescription);for (Observer observer : observers) {observer.receiveTask(taskDescription);}}@Overridepublic void receiveReport(String report) {System.out.println("朱元璋收到反馈:" + report);}public void registerObserver(Observer observer) {observers.add(observer);}public void removeObserver(Observer observer) {observers.remove(observer);} } - 定义具体实行监听示例锦衣卫(二虎小弟):锦衣卫作为观察者,负责监视刘伯温的行动,并向朱元璋汇报。
public class JinyiWei implements Observer {private String name;private Subject zhuYuanZhang;public JinyiWei(String name, Subject zhuYuanZhang) {this.name = name;this.zhuYuanZhang = zhuYuanZhang;}@Overridepublic void receiveTask(String taskDescription) {System.out.println(name + " 接收到任务:" + taskDescription);// 模拟监听过程String report = performMonitoring(taskDescription);// 向朱元璋反馈信息reportBack(report);}@Overridepublic void reportBack(String report) {zhuYuanZhang.receiveReport(report);}private String performMonitoring(String taskDescription) {// 模拟监听过程return "监听结果:刘伯温 " + taskDescription;} } - 开始监听刘伯温
public class Main {public static void main(String[] args) {// 创建朱元璋(主题)Subject zhuYuanZhang = new ZhuYuanZhang();// 创建锦衣卫(观察者)Observer jinyiWei1 = new JinyiWei("锦衣卫 A", zhuYuanZhang);Observer jinyiWei2 = new JinyiWei("锦衣卫 B", zhuYuanZhang);// 注册锦衣卫zhuYuanZhang.registerObserver(jinyiWei1);zhuYuanZhang.registerObserver(jinyiWei2);// 朱元璋下达监听任务zhuYuanZhang.assignTask("监视刘伯温在青田老家的行动");zhuYuanZhang.assignTask("监视刘伯温准备回京的行动");// 移除一个锦衣卫zhuYuanZhang.removeObserver(jinyiWei1);// 再次下达监听任务zhuYuanZhang.assignTask("监视刘伯温抵达京城后的行动");} }// 朱元璋下达监听任务:监视刘伯温在青田老家的行动 锦衣卫 A 接收到任务:监视刘伯温在青田老家的行动 锦衣卫 B 接收到任务:监视刘伯温在青田老家的行动 朱元璋收到反馈:监听结果:刘伯温 在青田老家与旧部密谈 朱元璋收到反馈:监听结果:刘伯温 在青田老家与旧部密谈 朱元璋下达监听任务:监视刘伯温准备回京的行动 锦衣卫 A 接收到任务:监视刘伯温准备回京的行动 锦衣卫 B 接收到任务:监视刘伯温准备回京的行动 朱元璋收到反馈:监听结果:刘伯温 准备回京,行动异常 朱元璋收到反馈:监听结果:刘伯温 准备回京,行动异常 朱元璋下达监听任务:监视刘伯温抵达京城后的行动 锦衣卫 B 接收到任务:监视刘伯温抵达京城后的行动 朱元璋收到反馈:监听结果:刘伯温 抵达京城后与胡惟庸密会
3.2 Spring 事件机制与消息队列(MQ)的观察者模式
在实际应用中,我们一般不自己实现观察者模式,多数使用到的是Spring 事件机制和消息队列(MQ)。
3.2.1 Spring 中观察者模式的四个角色
-
事件(Event):
-
定义:
ApplicationEvent是所有事件对象的父类,继承自 JDK 的EventObject。 -
作用:所有自定义事件都需要继承
ApplicationEvent,并通过getSource()方法获取事件源。 -
内置事件:Spring 提供了多种内置事件,如
ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent和RequestHandledEvent。
-
-
事件监听器(Listener):
-
定义:
ApplicationListener是事件监听器接口,继承自 JDK 的EventListener。 -
作用:监听器通过实现
onApplicationEvent(ApplicationEvent event)方法来处理事件。当事件发生时,Spring 会调用此方法。 -
实现方式:可以通过实现
ApplicationListener接口或使用@EventListener注解来定义监听器。
-
-
事件源(Event Source):
-
定义:
ApplicationContext是 Spring 的核心容器,也是事件的发布者。 -
作用:
ApplicationContext继承自ApplicationEventPublisher,通过publishEvent(Object event)方法发布事件。 -
发布方式:事件可以由任何组件通过调用
ApplicationContext的publishEvent方法发布。
-
-
事件管理器(Event Multicaster):
-
定义:
ApplicationEventMulticaster是事件管理器,负责事件监听器的注册和事件的广播。 -
作用:当
ApplicationContext发布事件时,ApplicationEventMulticaster负责将事件广播给所有注册的监听器。 -
注册方式:监听器可以通过注解(如
@EventListener)或通过实现ApplicationListener接口并注册到ApplicationContext中。
-
3.2.2 Spring事件与MQ的对比
| 场景 | Spring 事件机制 | 消息队列(MQ) |
|---|---|---|
| 单体应用内部事件传递 | ✅ 适合,低延迟 | ❌ 过于复杂,性能未必优于直接调用 |
| 分布式系统通信 | ❌ 需额外实现跨容器事件传递 | ✅ 天然支持分布式,适合跨系统通信 |
| 高可靠性场景 | ❌ 容器故障可能导致事件丢失 | ✅ 提供持久化和重试机制,确保消息不丢失 |
| 大规模并发消息处理 | ❌ 不支持高并发场景 | ✅ 专为高并发设计,支持海量消息传递 |
| 事务一致性要求高的场景 | ❌ 无原生事务支持,需手动处理 | ✅ 提供事务机制(如 Kafka 事务 API) |
| 消息顺序严格要求的场景 | ❌ 无顺序性保障 | ✅ 支持消息顺序(如 Kafka 分区内消息有序) |
相关文章:
【设计模式-行为型】观察者模式
一、什么是观察者模式 说起观察者模式,不得不说一位观察者模式的高级应用者,朱元璋。不知道大家有没有看过胡军演的电视剧《朱元璋》。这部剧背景是元朝末年,天下大乱,朱元璋自幼父母双亡,沦为乞丐,后遁入空…...
从理论到实践:Django 业务日志配置与优化指南
在现代 Web 开发中,日志记录是确保系统可维护性和可观测性的重要手段。通过合理的日志配置,我们可以快速定位问题、分析系统性能,并进行安全审计。本文将围绕 Django 框架,详细介绍如何配置和优化业务日志,确保开发环境和生产环境都能高效地记录和管理日志。 © ivwdc…...
Linux下php8安装phpredis扩展的方法
Linux下php8安装phpredis扩展的方法 下载redis扩展执行安装编辑php.ini文件重启php-fpmphpinfo 查看 下载redis扩展 前提是已经安装好redis服务了 php-redis下载地址 https://github.com/phpredis/phpredis 执行命令 git clone https://github.com/phpredis/phpredis.git执行…...
Flink运行时架构
一、系统架构 1)作业管理器(JobManager) JobManager是一个Flink集群中任务管理和调度的核心,是控制应用执行的主进程。也就是说,每个应用都应该被唯一的JobManager所控制执行。 JobManger又包含3个不同的组件。 &am…...
JupyterLab 安装以及部分相关配置
安装 JupyterLab pip install jupyter启动 JupyterLab jupyter lab [--port <指定的端口号>] [--no-browser] # --port 指定端口 # --no-browser 启动时不打开浏览器安装中文 首先安装中文包 pip install jupyterlab-language-pack-zh-CN安装完成后重启 JupyterLab 选…...
PC端实现PDF预览(支持后端返回文件流 || 返回文件URL)
一、使用插件 插件名称:vue-office/pdf 版本:2.0.2 安装插件:npm i vue-office/pdf^2.0.2 1、“vue-office/pdf”: “^2.0.2”, 2、 npm i vue-office/pdf^2.0.2 二、代码实现 // 引入组件 (在需要使用的页面中直接引入&#x…...
大模型 / 智能体在智能运维领域的应用总结与发展趋势概述
智能体 智能运维 ? 回顾大模型的发展 大模型的发展在过去两年间呈现出爆炸式的增长,成为推动人工智能领域快速进步的关键力量。 2023年3月:百度发布了其知识增强的大语言模型产品“文心一言”,这标志着国内AI大模型产业竞争的…...
uniapp 在线更新应用
在线更新应用及进度条显示 1.比较现安装手机中的apk 与线上apk的版本 getVersion(){var newVersionuni.getStorageSync("newVersion").split(".")var versionplus.runtime.version.split(".") // 获取手机安装的版本var versionNum""…...
AIGC视频生成模型:ByteDance的PixelDance模型
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍ByteDance的视频生成模型PixelDance,论文于2023年11月发布,模型上线于2024年9月,同时期上线的模型还有Seaweed&…...
python远程获取数据库中的相关数据并存储至json文件
1. conn中的5个变量的含义: ① Driver:数据库驱动程序,我使用的是SQL Server数据库。 ② Server:数据库所在的服务器地址。 ③ Database:要连接的数据库的名称。 ④ UID:登录 SQL Server 数据库的用户名…...
Kubernetes v1.28.0安装dashboard v2.6.1(k8s图形化操作界面)
准备工作 Kubernetes v1.28.0搭建教程请参考:Kubernetes v1.28.0集群快速搭建教程-CSDN博客 查看当前集群nodes都是ready状态 查看当前pods都是running状态 下载并修改配置文件 下载 recommended.yaml ,下载好之后,进入文件编辑 下载地址…...
详解三种常用标准化:Batch Norm、Layer Norm和RMSNorm
在深度学习中,标准化技术是提升模型训练速度、稳定性和性能的重要手段。本文将详细介绍三种常用的标准化方法:Batch Normalization(批量标准化)、Layer Normalization(层标准化)和 RMS Normalization&#…...
linux+docker+nacos+mysql部署
一、下载 docker pull mysql:5.7 docker pull nacos/nacos-server:v2.2.2 docker images 二、mysql部署 1、创建目录存储数据信息 mkdir ~/mysql cd ~/mysql 2、运行 MySQL 容器 docker run -id \ -p 3306:3306 \ --name mysql \ -v $PWD/conf:/etc/mysql/conf.d \ -v $PWD/…...
如何实现gitlab和jira连通
将 GitLab 和 Jira 集成起来可以实现开发任务与代码变更的联动,提高团队协作效率。以下是实现两者连通的详细步骤: 1. 确保必要条件 在进行集成之前,确保以下条件满足: 你有 GitLab 和 Jira 的管理员权限。Jira 是 Jira Cloud 或…...
利用ML.NET精准提取人名
在当今信息爆炸的时代,文本处理任务层出不穷,其中人名提取作为基础且重要的工作,广泛应用于信息检索、社交网络分析、客户关系管理等领域。随着人工智能不断进步,ML.NET作为微软推出的开源机器学习框架,为开发者提供了…...
Node.js的解释
1. Node.js 入门教程 1.1 什么是 Node.js? 1.1.1 Node.js 是什么? Node.js 是一个基于 JavaScript 的开源服务器端运行时环境,允许开发者用 JavaScript 编写服务器端代码。与传统的前端 JavaScript 主要运行在浏览器端不同,Nod…...
Macos下交叉编译安卓的paq8px压缩算法
官方没有android的编译方法,自己编写脚本在macos下交叉编译. 下载源码: git clone https://github.com/hxim/paq8px.git 稍旧的ndk并不能编译成功,需要下载最新的ndkr27c, 最后是使用clang来编译。 编译build.sh export ANDROID_NDK/Vol…...
如何在data.table中处理缺失值
📊💻【R语言进阶】轻松搞定缺失值,让数据清洗更高效! 👋 大家好呀!今天我要和大家分享一个超实用的R语言技巧——如何在data.table中处理缺失值,并且提供了一个自定义函数calculate_missing_va…...
从零安装 LLaMA-Factory 微调 Qwen 大模型成功及所有的坑
文章目录 从零安装 LLaMA-Factory 微调 Qwen 大模型成功及所有的坑一 参考二 安装三 启动准备大模型文件 四 数据集(关键)!4.1 Alapaca格式4.2 sharegpt4.3 在 dataset_info.json 中注册4.4 官方 alpaca_zh_demo 例子 999条数据, 本机微调 5分…...
SQL-leetcode—1164. 指定日期的产品价格
1164. 指定日期的产品价格 产品数据表: Products ---------------------- | Column Name | Type | ---------------------- | product_id | int | | new_price | int | | change_date | date | ---------------------- (product_id, change_date) 是此表的主键(具…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
MeshGPT 笔记
[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭!_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...
