【设计模式-行为型】观察者模式
一、什么是观察者模式
说起观察者模式,不得不说一位观察者模式的高级应用者,朱元璋。不知道大家有没有看过胡军演的电视剧《朱元璋》。这部剧背景是元朝末年,天下大乱,朱元璋自幼父母双亡,沦为乞丐,后遁入空门,最终加入义军,南征北战,一步步登上历史舞台。剧中对朱元璋的刻画非常细腻,展现了他从底层一步步走向权力巅峰的过程。
在剧中,朱元璋为了巩固皇权,设立了锦衣卫。这一情节反映了朱元璋对权力的绝对掌控和对潜在威胁的高度警惕。其中有一个电影情结,让我记忆深刻:
监视闲赋在家的刘伯温 -------情节还原
-
刘伯温罢官回乡:朱元璋在经历了杨宪事件后变得更加多疑,开始怀疑刘伯温及其他官员。为了加强对官员的监控,朱元璋密令二虎组建了一支秘密队伍,命名为“锦衣卫”,专门监视所有皇孙臣子。刘伯温因与朱元璋政见不合,被朱元璋拒见并逐渐被孤立。刘伯温意识到朱元璋可能随时会治他的罪,于是提前写好了遗嘱。
-
归乡途中被监视:刘伯温在归乡途中被锦衣卫检校吴风半路拦截,奉命护送他回青田老家。刘伯温这才断定朱元璋可能要对他不利。在护送过程中,吴风等人虽然表面上照顾刘伯温父子的起居,但实际上一直在监视他们的行动和对话。刘伯温等待着吴风的诛杀,但直到抵达青田老家,吴风也没有动手,这让刘伯温感到非常诧异。
-
后续发展:朱元璋不断赏赐刘伯温,试图通过这种方式让刘伯温回京。刘伯温最终决定回京,但吴风再次出现,奉命护送他返回京城。
这一情节生动地体现了观察者模式的核心逻辑:通过“观察者”(锦衣卫)监视“被观察对象”(刘伯温),并将情报汇报给“主题”(朱元璋)。这种模式不仅巩固了朱元璋的皇权,还通过动态监控和及时反应,确保了明朝初年的政治稳定。基于上面的例子,我们来解释一下什么是观察者模式:观察者模式是一种行为型设计模式,它通过定义对象之间的依赖关系,使得当一个对象(主题)的状态发生变化时,所有依赖于它的对象(观察者)都会自动得到通知并更新。这种模式非常适合用于“一对多”的依赖关系,其中一个对象的状态变化需要通知多个其他对象。
二、为什么用观察者模式
通过这个例子来说明一下为什么要使用观察者模式呢(朱元璋为啥使用锦衣卫)
-
解耦合:观察者模式使得主题和被观察对象之间松耦合,主题不需要直接与被观察对象互动,而是通过观察者获取信息。朱元璋(主题)不需要直接与刘伯温(被观察对象)互动,而是通过锦衣卫(观察者)来获取信息。这种间接的监控方式使得朱元璋和刘伯温之间保持了松耦合关系。朱元璋不需要了解刘伯温的具体行动细节,只需要通过锦衣卫获取关键信息,从而减少了直接干预带来的风险。
-
动态监控:观察者模式支持动态监控,主题可以实时获取被观察对象的状态变化,并及时做出反应。朱元璋需要实时掌握刘伯温的动态,以便在必要时采取行动。通过锦衣卫的监视,朱元璋可以在刘伯温有任何异常行为时迅速做出反应,确保皇权的稳固。
-
集中管理:观察者模式通过统一的接口管理多个观察者,使得主题能够集中管理所有观察者的行为。这提高了系统的整体协调性和一致性。朱元璋通过锦衣卫统一管理对刘伯温的监视,确保所有信息都能集中汇报到他这里。这种集中管理的方式使得朱元璋能够全面掌握局势,避免信息碎片化,从而更好地做出决策。
-
扩展性:观察者模式允许动态地添加或删除观察者,而不需要修改主题的代码。这使得系统在运行时可以根据需要灵活调整监控范围和方式。朱元璋还可以添加监控对象胡惟庸,后续剧情。
-
广播通信:观察者模式支持一对多的广播通信机制,当主题的状态发生变化时,所有观察者都会收到通知。这使得系统能够高效地传递信息,减少重复劳动。指令统一由朱元璋下达,广播给锦衣卫。
三、观察者模式示例
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) 是此表的主键(具…...

[Day 15]54.螺旋矩阵(简单易懂 有画图)
今天我们来看这道螺旋矩阵,和昨天发的题很类似。没有技巧,全是循环。小白也能懂~ 力扣54.螺旋矩阵 题目描述: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: …...

HTTP 配置与应用(不同网段)
想做一个自己学习的有关的csdn账号,努力奋斗......会更新我计算机网络实验课程的所有内容,还有其他的学习知识^_^,为自己巩固一下所学知识,下次更新校园网设计。 我是一个萌新小白,有误地方请大家指正,谢谢…...

Quartus:开发使用及 Tips 总结
Quartus是Altera(现已被Intel收购)推出的一款针对其FPGA产品的综合性开发环境,用于设计、仿真和调试数字电路。以下是使用Quartus的一些总结和技巧(Tips),帮助更高效地进行FPGA项目开发: 这里写目录标题 使用总结TIPS…...

VSCode下EIDE插件开发STM32
VSCode下STM32开发环境搭建 本STM32教程使用vscode的EIDE插件的开发环境,完全免费,有管理代码文件的界面,不需要其它IDE。 视频教程见本人的 VSCodeEIDE开发STM32 安装EIDE插件 Embedded IDE 嵌入式IDE 这个插件可以帮我们管理代码文件&am…...

Golang并发机制及CSP并发模型
Golang 并发机制及 CSP 并发模型 Golang 是一门为并发而生的语言,其并发机制基于 CSP(Communicating Sequential Processes,通信顺序过程) 模型。CSP 是一种描述并发系统中交互模式的正式语言,强调通过通信来共享内存…...

HTML 文本格式化详解
在网页开发中,文本内容的呈现方式直接影响用户的阅读体验。HTML 提供了多种文本格式化元素,可以帮助我们更好地控制文本的显示效果。本文将详细介绍 HTML 中的文本格式化元素及其使用方法,帮助你轻松实现网页文本的美化。 什么是 HTML 文本格…...

我谈《概率论与数理统计》的知识体系
学习《概率论与数理统计》二十多年后,在廖老师的指导下,才厘清了各章之间的关系。首先,这是两个学科综合的一门课程,这一门课程中还有术语冲突的问题。这一门课程一条线两个分支,脉络很清晰。 概率论与统计学 概率论…...

五、华为 RSTP
RSTP(Rapid Spanning Tree Protocol,快速生成树协议)是 STP 的优化版本,能实现网络拓扑的快速收敛。 一、RSTP 原理 快速收敛机制:RSTP 通过引入边缘端口、P/A(Proposal/Agreement)机制等&…...

基于Java Web的网上房屋租售网站
内容摘要 本毕业设计题目为《基于Java Web的网上房屋租售网站》,是在信息化时代下充分利用互联网对传统房屋租售方式进行创新,在互联网上进行房屋租售突破了传统方式的局限性。对于房屋租售的当事人都提供了极大的便利。本稳针对了实际用户需求…...

Pyside6(PyQT5)中的QTableView与QSqlQueryModel、QSqlTableModel的联合使用
QTableView 是QT的一个强大的表视图部件,可以与模型结合使用以显示和编辑数据。QSqlQueryModel、QSqlTableModel 都是用于与 SQL 数据库交互的模型,将二者与QTableView结合使用可以轻松地展示和编辑数据库的数据。 QSqlQueryModel的简单应用 import sys from PySid…...