【设计模式-行为型】观察者模式

一、什么是观察者模式

        说起观察者模式,不得不说一位观察者模式的高级应用者,朱元璋。不知道大家有没有看过胡军演的电视剧《朱元璋》。这部剧背景是元朝末年,天下大乱,朱元璋自幼父母双亡,沦为乞丐,后遁入空门,最终加入义军,南征北战,一步步登上历史舞台。剧中对朱元璋的刻画非常细腻,展现了他从底层一步步走向权力巅峰的过程。

        在剧中,朱元璋为了巩固皇权,设立了锦衣卫。这一情节反映了朱元璋对权力的绝对掌控和对潜在威胁的高度警惕。其中有一个电影情结,让我记忆深刻:

监视闲赋在家的刘伯温 -------情节还原

  1. 刘伯温罢官回乡:朱元璋在经历了杨宪事件后变得更加多疑,开始怀疑刘伯温及其他官员。为了加强对官员的监控,朱元璋密令二虎组建了一支秘密队伍,命名为“锦衣卫”,专门监视所有皇孙臣子。刘伯温因与朱元璋政见不合,被朱元璋拒见并逐渐被孤立。刘伯温意识到朱元璋可能随时会治他的罪,于是提前写好了遗嘱。

  2. 归乡途中被监视:刘伯温在归乡途中被锦衣卫检校吴风半路拦截,奉命护送他回青田老家。刘伯温这才断定朱元璋可能要对他不利。在护送过程中,吴风等人虽然表面上照顾刘伯温父子的起居,但实际上一直在监视他们的行动和对话。刘伯温等待着吴风的诛杀,但直到抵达青田老家,吴风也没有动手,这让刘伯温感到非常诧异。

  3. 后续发展:朱元璋不断赏赐刘伯温,试图通过这种方式让刘伯温回京。刘伯温最终决定回京,但吴风再次出现,奉命护送他返回京城。

        这一情节生动地体现了观察者模式的核心逻辑:通过“观察者”(锦衣卫)监视“被观察对象”(刘伯温),并将情报汇报给“主题”(朱元璋)。这种模式不仅巩固了朱元璋的皇权,还通过动态监控和及时反应,确保了明朝初年的政治稳定。基于上面的例子,我们来解释一下什么是观察者模式:观察者模式是一种行为型设计模式,它通过定义对象之间的依赖关系,使得当一个对象(主题)的状态发生变化时,所有依赖于它的对象(观察者)都会自动得到通知并更新。这种模式非常适合用于“一对多”的依赖关系,其中一个对象的状态变化需要通知多个其他对象

二、为什么用观察者模式

        通过这个例子来说明一下为什么要使用观察者模式呢(朱元璋为啥使用锦衣卫)

  1. 解耦合:观察者模式使得主题和被观察对象之间松耦合,主题不需要直接与被观察对象互动,而是通过观察者获取信息。朱元璋(主题)不需要直接与刘伯温(被观察对象)互动,而是通过锦衣卫(观察者)来获取信息。这种间接的监控方式使得朱元璋和刘伯温之间保持了松耦合关系。朱元璋不需要了解刘伯温的具体行动细节,只需要通过锦衣卫获取关键信息,从而减少了直接干预带来的风险。

  2. 动态监控:观察者模式支持动态监控,主题可以实时获取被观察对象的状态变化,并及时做出反应。朱元璋需要实时掌握刘伯温的动态,以便在必要时采取行动。通过锦衣卫的监视,朱元璋可以在刘伯温有任何异常行为时迅速做出反应,确保皇权的稳固。

  3. 集中管理:观察者模式通过统一的接口管理多个观察者,使得主题能够集中管理所有观察者的行为。这提高了系统的整体协调性和一致性。朱元璋通过锦衣卫统一管理对刘伯温的监视,确保所有信息都能集中汇报到他这里。这种集中管理的方式使得朱元璋能够全面掌握局势,避免信息碎片化,从而更好地做出决策。

  4. 扩展性:观察者模式允许动态地添加或删除观察者,而不需要修改主题的代码。这使得系统在运行时可以根据需要灵活调整监控范围和方式。朱元璋还可以添加监控对象胡惟庸,后续剧情。

  5. 广播通信:观察者模式支持一对多的广播通信机制,当主题的状态发生变化时,所有观察者都会收到通知。这使得系统能够高效地传递信息,减少重复劳动。指令统一由朱元璋下达,广播给锦衣卫。

三、观察者模式示例

3.1 锦衣卫Demo

        下面让我们来用代码还原一下场景:

  1. 定义锦衣卫行为(接收任务,反馈监听信息)
    public interface Observer {void receiveTask(String taskDescription); // 接收监听任务void reportBack(String report); // 向朱元璋反馈信息
    }
  2. 定义主题类朱元璋的行为
    
    import java.util.List;public interface Subject {void assignTask(String taskDescription); // 下达监听任务void receiveReport(String report); // 接收反馈
    }
  3. 定义具体主题类朱元璋
    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);}
    }
  4. 定义具体实行监听示例锦衣卫(二虎小弟):锦衣卫作为观察者,负责监视刘伯温的行动,并向朱元璋汇报。
    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;}
    }
  5. 开始监听刘伯温
    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 中观察者模式的四个角色

  1. 事件(Event)

    • 定义ApplicationEvent 是所有事件对象的父类,继承自 JDK 的 EventObject

    • 作用:所有自定义事件都需要继承 ApplicationEvent,并通过 getSource() 方法获取事件源。

    • 内置事件:Spring 提供了多种内置事件,如 ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent

  2. 事件监听器(Listener)

    • 定义ApplicationListener 是事件监听器接口,继承自 JDK 的 EventListener

    • 作用:监听器通过实现 onApplicationEvent(ApplicationEvent event) 方法来处理事件。当事件发生时,Spring 会调用此方法。

    • 实现方式:可以通过实现 ApplicationListener 接口或使用 @EventListener 注解来定义监听器。

  3. 事件源(Event Source)

    • 定义ApplicationContext 是 Spring 的核心容器,也是事件的发布者。

    • 作用ApplicationContext 继承自 ApplicationEventPublisher,通过 publishEvent(Object event) 方法发布事件。

    • 发布方式:事件可以由任何组件通过调用 ApplicationContextpublishEvent 方法发布。

  4. 事件管理器(Event Multicaster)

    • 定义ApplicationEventMulticaster 是事件管理器,负责事件监听器的注册和事件的广播。

    • 作用:当 ApplicationContext 发布事件时,ApplicationEventMulticaster 负责将事件广播给所有注册的监听器。

    • 注册方式:监听器可以通过注解(如 @EventListener)或通过实现 ApplicationListener 接口并注册到 ApplicationContext 中。

3.2.2 Spring事件与MQ的对比

场景Spring 事件机制消息队列(MQ)
单体应用内部事件传递✅ 适合,低延迟❌ 过于复杂,性能未必优于直接调用
分布式系统通信❌ 需额外实现跨容器事件传递✅ 天然支持分布式,适合跨系统通信
高可靠性场景❌ 容器故障可能导致事件丢失✅ 提供持久化和重试机制,确保消息不丢失
大规模并发消息处理❌ 不支持高并发场景✅ 专为高并发设计,支持海量消息传递
事务一致性要求高的场景❌ 无原生事务支持,需手动处理✅ 提供事务机制(如 Kafka 事务 API)
消息顺序严格要求的场景❌ 无顺序性保障✅ 支持消息顺序(如 Kafka 分区内消息有序)

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

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

相关文章

考研机试:学分绩点

描述 北京大学对本科生的成绩施行平均学分绩点制&#xff08;GPA&#xff09;。 既将学生的实际考分根据不同的学科的不同学分按一定的公式进行计算。 公式如下&#xff1a; 一门课程的学分绩点 该课绩点 该课学分 总评绩点 所有学科学分绩点之和 / 所有课程学分之和 …

CentOS9 安装Docker+Dpanel+onlyoffice(https、更改字体、字号、去除限制)的避坑笔记

CentOS9 安装Dockeronlyoffice&#xff08;https、更改字体、字号、去除文件大小限制&#xff09;的避坑笔记 一、安装Docker二、更新docker镜像源&#xff1a;三、安装Dpanel四、安装onlyoffice&#xff08;开启https及一些碰到的问题&#xff09;五、更改字体和字号六、去除限…

【玩转全栈】----YOLO8训练自己的模型并应用

继上篇&#xff1a; 【玩转全栈】---基于YOLO8的图片、视频目标检测-CSDN博客 相信大家已经可以训练一些图片和视频了&#xff0c;接下来我将为大家介绍如何训练自己的特定模型&#xff0c;并用其进行检测 目录 准备数据 图片数据 标识数据 配置文件 运行 测试训练结果 存在的问…

OpenCV文字绘制支持中文显示

OpenCV版本&#xff1a;4.4 IDE&#xff1a;VS2019 功能描述 OpenCV绘制文本的函数putText()不支持中文的显示&#xff0c;网上很多方法推荐的都是使用FreeType来支持&#xff0c;FreeType是什么呢&#xff1f;FreeType的官网上有介绍 FreeType官网 https://www.freetype.or…

梯度下降法 (Gradient Descent) 算法详解及案例分析

梯度下降法 (Gradient Descent) 算法详解及案例分析 目录 梯度下降法 (Gradient Descent) 算法详解及案例分析1. 引言2. 梯度下降法 (Gradient Descent) 算法原理2.1 基本概念2.2 算法步骤2.3 梯度下降法的变种3. 梯度下降法的优势与局限性3.1 优势3.2 局限性4. 案例分析4.1 案…

GPSd定时检测保活TCP GPS源

为了在 TCP GPS 源丢失连接时自动重新连接&#xff0c;可以编写一个监控脚本&#xff0c;定期检查 gpspipe 输出中的 TCP 源数据是否存在。如果检测到丢失&#xff0c;则使用 gpsdctl 或直接命令重新添加 TCP 源。 1、工具 检查并安装必要工具&#xff0c;本例需要使用 gpspi…

我谈《概率论与数理统计》的知识体系

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

ElasticSearch JavaRestClient查询之快速入门

文章目录 查询操作流程概述构建并发起请求1. 创建请求对象2. 设置请求体3. 发送请求 查询结果的解析1. 解析结果结构2. 获取总条数3. 获取命中的数据 完整示例代码总结 查询操作流程概述 Elasticsearch 查询操作大致可以分为两个部分&#xff1a; 构建并发起请求&#xff1a;…

Quartus:开发使用及 Tips 总结

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

elementUI Table组件实现表头吸顶效果

需求描述 当 table 内容过多的时候&#xff0c;页面上滑滚动&#xff0c;表头的信息也会随着被遮挡&#xff0c;无法将表头信息和表格内容对应起来&#xff0c;需要进行表头吸顶 开始编码&#x1f4aa; 环境&#xff1a;vue2.6、element UI step1&#xff1a; 给el-table__h…

用于牙科的多任务视频增强

Multi-task Video Enhancement for Dental Interventions 2022 miccai Abstract 微型照相机牢牢地固定在牙科手机上&#xff0c;这样牙医就可以持续地监测保守牙科手术的进展情况。但视频辅助牙科干预中的视频增强减轻了低光、噪音、模糊和相机握手等降低视觉舒适度的问题。…

Vue3轮播图左右联动

1、轮播图部分&#xff0c;右边鼠标移入&#xff0c;左边对应展示轮播图 可以在swiper 官网 Swiper中文网-轮播图幻灯片js插件,H5页面前端开发 选择vue中使用swiper npm i swiper 左右两边的联动&#xff1a;左边的轮播图和右边的小的列表他们的列表组成结构是一样的&#…

windows下本地部署安装hadoop+scala+spark-【不需要虚拟机】

注意版本依赖【本实验版本如下】 Hadoop 3.1.1 spark 2.3.2 scala 2.11 1.依赖环境 1.1 java 安装java并配置环境变量【如果未安装搜索其他教程】 环境验证如下&#xff1a; C:\Users\wangning>java -version java version "1.8.0_261" Java(TM) SE Runti…

go-zero框架基本配置和错误码封装

文章目录 加载配置信息配置 env加载.env文件配置servicecontext 查询数据生成model文件执行查询操作 错误码封装配置拦截器错误码封装 接上一篇&#xff1a;《go-zero框架快速入门》 加载配置信息 配置 env 在项目根目录下新增 .env 文件&#xff0c;可以配置当前读取哪个环…

2025 最新flutter面试总结

目录 1.Dart是值传递还是引用传递&#xff1f; 2.Flutter 是单引擎还是双引擎 3. StatelessWidget 和 StatefulWidget 在 Flutter 中有什么区别&#xff1f; 4.简述Dart语音特性 5. Navigator 是什么&#xff1f;在 Flutter 中 Routes 是什么&#xff1f; 6、Dart 是不是…

HarmonyOS Next构建工具 lycium 原理介绍

HarmonyOS Next构建工具 lycium 原理介绍 背景介绍 HarmonyOS Next中很多系统API是以C接口提供&#xff0c;如果要使用C接口&#xff0c;必须要使用NAPI在ArkTS与C间交互&#xff0c;这种场景在使用DevEco-Studio中集成的交叉编译工具&#xff0c;以及cmake构建工具就完全够用…

最长递增——蓝桥杯

1.题目描述 在数列 a1​,a2​,⋯,an​ 中&#xff0c;如果ai​<ai1​<ai2​<⋯<aj​&#xff0c;则称 ai​ 至 aj​ 为一段递增序列&#xff0c;长度为 j−i1。 定一个数列&#xff0c;请问数列中最长的递增序列有多长。 输入描述 输入的第一行包含一个整数 n。…

【远程视频必备】Briefing:安全视频群聊让远程办公无忧

文章目录 前言1.关于briefing2.本地部署briefing3.使用briefing4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定briefing公网地址 前言 对于有远程办公或者身处异地与家人好友视频聊天需求的人来说&#xff0c;在享受高效沟通的同时&#xff0c;也或多或少会有对信息泄…

华为发展历程:战略转型与分析

纵观30多年的发展历程&#xff0c;在创始人任正非及创业团队奋力牵引下&#xff0c;全体员工共同奋斗&#xff0c;华为实现了从“一无所有”到“三分天下”、从“积极跟随者”到“行业领先者”的跨越式发展。 华为在业务战略上经历了数次变革&#xff0c;分别是从农村到城市&a…

热更新杂乱记

热更新主要有一个文件的MD5值的比对过程&#xff0c;期间遇到2个问题&#xff0c;解决起来花费了一点时间 1. png 和 plist 生成zip的时候再生成MD5值会发生变动。 这个问题解决起来有2种方案&#xff1a; &#xff08;1&#xff09;.第一个方案是将 png和plist的文件时间改…