设计模式-观察着模式
观察者模式
观察者模式 (Observer Pattern) 是一种行为型设计模式,它定义了对象之间一种一对多的依赖关系,当一个对象(称为主题或可观察者)的状态发生改变时,所有依赖于它的对象(称为观察者)都会得到通知并自动更新。
核心思想:
想象一下报纸订阅的场景:
-
报社 (Subject/Observable):负责出版报纸。
-
订阅者 (Observer):对报社的报纸感兴趣的人。
当报社出版了新一期的报纸(状态改变),它会通知所有订阅了这份报纸的订阅者。订阅者收到通知后,就可以去获取最新的报纸内容进行阅读(自动更新)。
观察者模式就是这样,它允许一个对象(主题)在状态改变时通知其他多个对象(观察者),而无需知道这些观察者的具体细节。
模式结构:
观察者模式通常包含以下角色:
-
主题 (Subject) / 可观察者 (Observable):
-
维护一个观察者列表。
-
提供用于注册 (attach/subscribe) 和注销 (detach/unsubscribe) 观察者的方法。
-
当其自身状态发生变化时,负责通知所有已注册的观察者。
-
通常是一个接口或抽象类,定义了上述方法。
-
-
具体主题 (ConcreteSubject):
-
实现了主题接口/继承了抽象主题类。
-
存储了具体的状态,当状态改变时,会通知所有注册的观察者。
-
是实际的“被观察”对象。
-
-
观察者 (Observer):
-
定义了一个更新接口 (通常是一个名为 update() 的方法),当接收到主题的通知时,该方法被调用。
-
通常是一个接口或抽象类。
-
-
具体观察者 (ConcreteObserver):
-
实现了观察者接口/继承了抽象观察者类。
-
维护一个指向具体主题对象的引用(或者在 update() 方法中接收主题对象或其状态作为参数),以便在收到更新通知时可以获取主题的状态。
-
实现 update() 方法,根据主题状态的改变来执行相应的操作。
-
示意图:
+---------------------+ maintains a list of +--------------------+ | Subject |<--------------------------------| Observer | | (Observable) | | (e.g., update()) | +---------------------+ +--------------------+^ ^| implements | implements +---------------------+ +--------------------+ | ConcreteSubject |--------------------------------->| ConcreteObserverA | | - state | notifies +--------------------+ | - registerObserver()| +--------------------+ | - removeObserver() | | ConcreteObserverB | | - notifyObservers() | +--------------------+ +---------------------+
工作流程:
-
具体观察者对象将自己注册到具体主题对象中。
-
当具体主题对象的状态发生改变时,它会调用其 notifyObservers() 方法。
-
notifyObservers() 方法会遍历已注册的观察者列表,并调用每个观察者的 update() 方法。
-
具体观察者在其 update() 方法中,通常会从主题对象那里获取最新的状态,并根据这个状态执行相应的更新操作。
优点:
-
松耦合 (Loose Coupling):
-
主题和观察者之间是松耦合的。 主题只知道它有一系列观察者(实现了某个接口),但不需要知道观察者的具体类型或它们如何处理通知。
-
观察者之间也是松耦合的。 它们各自独立地响应主题的变化。
-
-
易于扩展:
-
可以很容易地增加新的观察者,而无需修改主题的代码。
-
也可以很容易地增加新的主题类型。
-
-
支持广播通信:主题可以向所有注册的观察者广播状态变化。
-
符合开闭原则:对扩展开放(可以增加新的观察者),对修改关闭(通常不需要修改现有主题或观察者的核心代码来添加新的观察者)。
缺点:
-
可能导致意外的更新:如果一个主题有大量的观察者,或者观察者的更新逻辑比较复杂,通知和更新过程可能会比较耗时。
-
更新顺序问题:在某些情况下,观察者接收通知的顺序可能很重要,但标准的观察者模式通常不保证特定的通知顺序。如果需要顺序,可能需要额外的逻辑或对模式进行扩展。
-
内存泄漏风险:如果观察者在不再需要时没有从主题中正确注销,主题可能会一直持有对这些观察者的引用,导致它们无法被垃圾回收,从而引发内存泄漏(特别是在观察者生命周期比主题短的情况下)。
-
级联更新 (Cascading Updates):一个观察者的更新操作可能会触发另一个观察者的更新,甚至可能导致循环依赖和无限更新(需要小心设计)。
适用场景:
-
当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
-
当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换言之,不希望这些对象是紧密耦合的。
-
在GUI事件处理中非常常见(例如,按钮点击事件,模型-视图-控制器 (MVC) 架构中的模型和视图之间的交互)。
-
消息队列、发布/订阅系统。
-
当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
代码示例 (Java - 使用内置的 java.util.Observable 和 java.util.Observer,虽然它们在 Java 9 中被标记为 @Deprecated,但原理相同,也可以自己实现):
自己实现一个简单的观察者模式:
Subject (接口):
import java.util.ArrayList; import java.util.List; // 主题接口 interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers(); }
Observer (接口):
// 观察者接口 interface Observer {void update(String message); // 简化:直接传递消息 }
ConcreteSubject:
class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();private String message; public void setMessage(String message) {this.message = message;System.out.println("Subject: State changed to '" + message + "'");notifyObservers(); // 状态改变时通知观察者} @Overridepublic void registerObserver(Observer o) {observers.add(o);} @Overridepublic void removeObserver(Observer o) {observers.remove(o);} @Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(message);}} }
ConcreteObserver:
class ConcreteObserver implements Observer {private String name; public ConcreteObserver(String name) {this.name = name;} @Overridepublic void update(String message) {System.out.println(name + " received update: '" + message + "'");} }
客户端代码:
public class ObserverDemo {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject(); ConcreteObserver observer1 = new ConcreteObserver("Observer A");ConcreteObserver observer2 = new ConcreteObserver("Observer B"); // 注册观察者subject.registerObserver(observer1);subject.registerObserver(observer2); // 主题状态改变subject.setMessage("Hello Observers!"); System.out.println("\n--- Observer A unsubscribes ---");subject.removeObserver(observer1); // 主题状态再次改变subject.setMessage("Another update!");} }
输出:
Subject: State changed to 'Hello Observers!' Observer A received update: 'Hello Observers!' Observer B received update: 'Hello Observers!' --- Observer A unsubscribes --- Subject: State changed to 'Another update!' Observer B received update: 'Another update!'
关于 Java 内置的 Observable 和 Observer:
-
java.util.Observable 是一个类,而不是接口,这意味着具体主题必须继承它,限制了其复用性(因为 Java 是单继承)。
-
java.util.Observer 是一个接口。
-
Observable 的 notifyObservers() 方法可以传递一个参数 (arg),观察者的 update(Observable o, Object arg) 方法会接收到主题对象和这个参数。
-
它们被标记为 @Deprecated 是因为其设计上的一些限制(例如 Observable 是类,以及 setChanged() 方法需要显式调用才能使 notifyObservers() 生效)。现代 Java 中更推荐使用更灵活的事件监听机制或反应式编程库 (如 RxJava, Project Reactor)。
总结:
观察者模式是一种实现对象间解耦的有效方式,使得当一个对象的状态改变时,能够自动通知依赖于它的其他对象。它在许多应用场景中都非常有用,尤其是在需要事件驱动或数据绑定机制时。理解其核心思想和优缺点,可以帮助你更好地设计和构建灵活、可维护的系统。
相关文章:
设计模式-观察着模式
观察者模式 观察者模式 (Observer Pattern) 是一种行为型设计模式,它定义了对象之间一种一对多的依赖关系,当一个对象(称为主题或可观察者)的状态发生改变时,所有依赖于它的对象(称为观察者)都…...

《架构即未来》笔记
思维导图 第一部分:可扩展性组织的人员配置 第二部分:构建可扩展的过程 第三部分:可扩展的架构方案 第四部分:其他的问题和挑战 资料 问软件工程研究所: https://www.sei.cmu.edu/ AKF公司博客: http://www.akfpart…...
stm32—ADC和DAC
ADC和DAC 在嵌入式系统中,微控制器经常需要与现实世界的模拟信号进行交互。STM32微控制器内置了模拟数字转换器(ADC)和数字模拟转换器(DAC),它们是实现这种交互的关键模块。 1. 模拟数字转换器(…...

ubuntu2404 gpu 没接显示器,如何保证远程显示的分辨率
1. 使用 xserver-xorg-video-dummy 创建虚拟显示器 如果系统在无物理显示器连接时无法识别显示输出,可以使用 xserver-xorg-video-dummy 驱动程序创建虚拟显示器。以下是设置步骤: 安装虚拟显示器驱动程序: sudo apt install xserver-xorg-v…...

【基于阿里云搭建数据仓库(离线)】使用UDTF时出现报错“FlatEventUDTF cannot be resolved”
目录 问题: 可能的原因有: 解决方法: 问题: 已经将包含第三方依赖的jar包上传到dataworks,并且成功注册函数,但是还是报错:“FlatEventUDTF cannot be resolved”,如下:…...

Pycharm的终端无法使用Anaconda命令行问题详细解决教程
很多初学者在Windows系统上安装了Anaconda后,在PyCharm终端中运行Conda命令时,会遇到以下错误: conda : 无法将“conda”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 请检查名称的拼写,如果包括路径,请确保…...

SAP学习笔记 - 开发24 - 前端Fiori开发 Filtering(过滤器),Sorting and Grouping(排序和分组)
上一章讲了SAP Fiori开发的表达式绑定,自定义格式化等内容。 SAP学习笔记 - 开发23 - 前端Fiori开发 Expression Binding(表达式绑定),Custom Formatters(自定义格式化)-CSDN博客 本章继续讲SAP Fiori开发…...
【Flask】:轻量级Python Web框架详解
什么是Flask? Flask是一个用Python编写的轻量级Web应用框架。它被称为"微框架"(microframework),因为它核心简单但可扩展性强,不强制使用特定的项目结构或库。Flask由Armin Ronacher开发,基于Werkzeug WSGI工具包和Jin…...

自建 dnslog 回显平台:渗透测试场景下的隐蔽回显利器
🔍 背景介绍 在渗透测试与红队评估过程中,DNS 外带(DNS Exfiltration) 是一种常见且隐蔽的通信通道。由于多数目标环境默认具备外网 DNS 解析能力,即便在 无回显、无文件上传权限 的条件下,仍可通过 DNS 请…...

Digital IC Design Flow
Flow介绍 1.设计规格 架构师根据市场需求制作算法模型(Algorithm emulation)及芯片架构(Chip architecture),确定芯片设计规格书(Chip design specification) 原型验证 原型验证(Prototype Validation)通常位于产品开发流程的前期阶段,主要是在设计和开发的初步阶…...

设备健康管理的范式革命:中讯烛龙全链路智能守护系统
当工业设备的“亚健康”状态导致隐性产能损失高达23%时,中讯烛龙推出 “感知-诊断-决策-闭环”四位一体解决方案,让设备全生命周期健康管理成为企业增长的隐形引擎。 一、行业痛点:传统运维的三大断层 1. 健康感知盲区 某风电场因无法捕…...

循环神经网络(RNN):从理论到翻译
循环神经网络(RNN)是一种专为处理序列数据设计的神经网络,如时间序列、自然语言或语音。与传统的全连接神经网络不同,RNN具有"记忆"功能,通过循环传递信息,使其特别适合需要考虑上下文或顺序的任…...

Redis:常用数据结构 单线程模型
🌈 个人主页:Zfox_ 🔥 系列专栏:Redis 🔥 常用数据结构 🐳 Redis 当中常用的数据结构如下所示: Redis 在底层实现上述数据结构的过程中,会在源码的角度上对于上述的内容进行特定的…...

夏普比率(Sharpe ratio)
具有投资常识的人都明白,投资光看收益是不够的,还要看承受的风险,也就是收益风险比。 夏普比率描述的正是这个概念,即每承受一单位的总风险,会产生多少超额的报酬。 用数学公式描述就是: 其中࿱…...

【优选算法】模拟 问题算法
一:替换所有的问号 class Solution { public:string modifyString(string s) {int n s.size();for(int i 0; i < n; i){if(s[i] ?){for(char ch a; ch < z; ch){if((i0 && ch !s[i1]) || (in-1 && ch ! s[i-1]) || ( i>0 &&…...

Flask+LayUI开发手记(八):通用封面缩略图上传实现
前一节做了头像上传的程序,应该说,这个程序编写和操作都相当繁琐,实际上,头像这种缩略图在很多功能中都会用到,屏幕界面有限,绝不会给那么大空间摆开那么大一个界面,更可能的处理,就…...

低代码采购系统搭建:鲸采云+能源行业订单管理自动化案例
在能源行业数字化转型浪潮下,某大型能源集团通过鲸采云低代码平台,仅用3周时间就完成了采购订单管理系统的定制化搭建。本文将揭秘这一成功案例的实施路径与关键成效。 项目背景与挑战 该企业面临: 供应商分散:200供应商使用不同…...

android关于pthread的使用过程
文章目录 简介代码流程pthread使用hello_test.cppAndroid.bp 编译过程报错处理验证过程 简介 android开发经常需要使用pthread来编写代码实现相关的业务需求 代码流程 pthread使用 需要查询某个linux函数的方法使用,可以使用man 函数名 // $ man pthread_crea…...
Faiss vs Milvus 深度对比:向量数据库技术选型指南
Faiss vs Milvus 深度对比:向量数据库技术选型指南 引言:向量数据库的时代抉择 在AI应用爆发的今天,企业和开发者面临着如何存储和检索海量向量数据的重大技术选择。作为当前最受关注的两大解决方案,Faiss和Milvus代表了两种不同…...
慢慢欣赏linux 之 last = switch_to(prev, next)分析
last switch_to(prev, next); 为什么需要定义last作为调用switch_to之前的prev的引用 原因如下: struct task_struct * switch_to(struct task_struct *prev,struct task_struct *next) {... ...return cpu_switch_to(prev, next);> .global cpu_switch_tocpu_…...

如何用 HTML 展示计算机代码
原文:如何用 HTML 展示计算机代码 | w3cschool笔记 (请勿将文章标记为付费!!!!) 在编程学习和文档编写过程中,清晰地展示代码是一项关键技能。HTML 作为网页开发的基础语言&#x…...

2025年ESWA SCI1区TOP,自适应学习粒子群算法AEPSO+动态周期调节灰色模型,深度解析+性能实测
目录 1.摘要2.粒子群算法PSO原理3.改进策略4.结果展示5.参考文献6.代码获取7.算法辅导应用定制读者交流 1.摘要 能源数据的科学预测对于能源行业决策和国家经济发展具有重要意义,尤其是短期能源预测,其精度直接影响经济运行效率。为了更好地提高预测模型…...

LeetCode - 53. 最大子数组和
目录 题目 Kadane 算法核心思想 Kadane 算法的步骤分析 读者可能的错误写法 正确的写法 题目 53. 最大子数组和 - 力扣(LeetCode) Kadane 算法核心思想 定义状态变量: currentSum: 表示以当前元素为结束的子数组的最大和。 maxSum: 记录全局最大…...
稻米分类和病害检测数据集(猫脸码客第237期)
稻米分类图像数据集:驱动农业智能化发展的核心资源 引言 在全球农业体系中,稻米作为最关键的粮食作物之一,其品种多样性为人类饮食提供了丰富选择。然而,传统稻米分类方法高度依赖人工经验,存在效率低、主观性强等缺…...
DOM(文档对象模型)深度解析
DOM(文档对象模型)深度解析 DOM 是 HTML/XML 文档的树形结构表示,提供了一套让 JavaScript 动态操作网页内容、结构和样式的接口。 一、DOM 核心概念 1. 节点(Node)类型 类型值说明示例ELEMENT_NODE1元素节点<div>, <p>TEXT_NODE3文本节点元素内的文字COMMEN…...
四、Sqoop 导入表数据子集
作者:IvanCodes 日期:2025年6月4日 专栏:Sqoop教程 当不需要将关系型数据库中的整个表一次性导入,而是只需要表中的一部分数据时,Sqoop 提供了多种方式来实现数据子集的导入。这通常通过过滤条件或选择特定列来完成。 …...

【读代码】从预训练到后训练:解锁语言模型推理潜能——Xiaomi MiMo项目深度解析
项目开源地址:https://github.com/XiaomiMiMo/MiMo 一、基本介绍 Xiaomi MiMo是小米公司开源的7B参数规模语言模型系列,专为复杂推理任务设计。项目包含基础模型(MiMo-7B-Base)、监督微调模型(MiMo-7B-SFT)和强化学习模型(MiMo-7B-RL)等多个版本。其核心创新在于通过…...

DROPP算法详解:专为时间序列和空间数据优化的PCA降维方案
DROPP (Dimensionality Reduction for Ordered Points via PCA) 是一种专门针对有序数据的降维方法。本文将详细介绍该算法的理论基础、实现步骤以及在降维任务中的具体应用。 在现代数据分析中,高维数据集普遍存在特征数量庞大的问题。这种高维特性不仅增加了计算…...
DeepSeek11-Ollama + Open WebUI 搭建本地 RAG 知识库全流程指南
🛠️ Ollama Open WebUI 搭建本地 RAG 知识库全流程指南 💻 一、环境准备 # 1. 安装 Docker 和 Docker Compose sudo apt update && sudo apt install docker.io docker-compose -y# 2. 添加用户到 docker 组(避免 sudo 权限&…...
【AI大模型】Transformer架构到底是什么?
引言 —— 想象一台能瞬间读懂整本《战争与和平》、精准翻译俳句中的禅意、甚至为你的设计草图生成前端代码的机器——这一切并非科幻,而是过去七年AI领域最震撼的技术革命:Transformer架构创造的奇迹。 当谷歌在2017年揭开Transformer的神秘面纱时&…...