当前位置: 首页 > news >正文

观察者模式是行为型设计模式的一种,其核心思想是定义对象间的一对多依赖关系

观察者模式是行为型设计模式的一种,其核心思想是定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。观察者模式也称为发布-订阅模式、模型-视图模式,实现了对象间的松耦合,提高了系统的可扩展性。

核心特点

主题和观察者之间是松耦合的,主题不需要知道观察者的具体实现细节

支持广播通信,主题状态变化时会自动通知所有注册的观察者

符合开闭原则,新增观察者不需要修改主题代码

可以实现动态的订阅和取消订阅,灵活管理观察者

核心角色

抽象主题(Subject):定义主题接口,提供注册、移除和通知观察者的方法

具体主题(Concrete Subject):实现抽象主题接口,维护观察者列表,状态变化时通知所有注册的观察者

抽象观察者(Observer):定义观察者接口,提供更新方法,在收到主题通知时执行相应操作

具体观察者(Concrete Observer):实现抽象观察者接口,存储与主题状态相关的信息,在收到通知时更新自身状态

二、适用场景

观察者模式通常应用于以下场景:

事件驱动系统:一个对象的状态变化需要通知其他多个对象,并且不知道有多少个对象需要通知

松耦合设计:需要在对象之间建立松耦合的关系,主题和观察者可以独立变化

多级触发场景:需要实现链式触发机制,一个操作触发一系列后续操作

消息通知系统:如消息推送、事件监听、订阅发布系统等

分布式系统:微服务之间的事件通知、状态同步等场景

UI交互:GUI中的组件事件监听,如按钮点击、文本变化等事件的处理

三、UML类图结构

┌─────────────────────────────┐
│ Subject │
├─────────────────────────────┤
│+attach(Observer) │
│+detach(Observer) │
│+notifyObservers() │
└─────────────────────────────┘


┌─────────────────────────────┐
│ ConcreteSubject │
├─────────────────────────────┤
│-observers: List │ │-state: Object │ ├─────────────────────────────┤ │+attach(Observer) │ │+detach(Observer) │ │+notifyObservers() │ │+getState() │ │+setState(state) │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ Observer │ ├─────────────────────────────┤ │+update(subject) │ └─────────────────────────────┘ ▲ ┌───────┴────────────────────┐ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ConcreteObserverA │ │ConcreteObserverB │ ├─────────────────────┤ ├─────────────────────┤ │+update(subject) │ │+update(subject) │ └─────────────────────┘ └─────────────────────┘

结构说明:具体主题维护观察者列表,提供注册和移除观察者的方法,当主题状态变化时,通知所有注册的观察者。观察者实现更新接口,在收到通知时根据主题状态更新自身。

四、代码实现示例

以天气站为例,天气数据变化时自动通知所有显示设备更新显示。

1. 抽象主题(天气主题)

2. 抽象观察者(天气观察者)

3. 具体主题(天气数据)

4. 具体观察者(显示设备)

5. 客户端使用示例

五、推模式 vs 拉模式

推模式

主题在通知观察者时,将所有需要的信息推送给观察者,不管观察者是否需要。上面的示例就是推模式,主题将温度、湿度、气压三个参数都推送给观察者。

优点:观察者使用方便,不需要主动获取数据

缺点:灵活性差,如果后续新增参数,需要修改所有观察者的更新方法

拉模式

主题在通知观察者时,只传递主题自身的引用,观察者根据需要主动从主题中拉取所需的数据。

优点:灵活性好,观察者可以按需获取数据,新增参数不需要修改观察者接口

缺点:观察者需要了解主题的结构,增加了耦合度

六、优缺点分析

七、典型应用案例

Java事件模型:AWT/Swing中的事件监听机制是观察者模式的典型应用,组件是主题,监听器是观察者

Spring事件机制:Spring中的ApplicationEvent和ApplicationListener实现了观察者模式,用于处理系统事件

消息队列:RabbitMQ、Kafka等消息中间件的发布订阅模式是观察者模式的分布式实现

ZooKeeper节点监听:ZooKeeper的Watcher机制使用观察者模式,节点状态变化时通知所有注册的Watcher

Android广播机制:Android中的BroadcastReceiver是观察者模式的应用,系统事件变化时通知所有注册的接收器

RxJava响应式编程:RxJava中的Observable和Observer是观察者模式的实现,用于处理异步数据流

微信公众号订阅:公众号是主题,订阅用户是观察者,公众号发布新文章时通知所有订阅用户

八、与其他模式的对比

九、注意事项

根据场景选择推模式或拉模式,推模式适合参数固定的场景,拉模式适合参数多变的场景

注意通知顺序,默认情况下观察者接收通知的顺序与注册顺序一致,如果需要特定顺序可以进行排序

避免循环依赖,主题和观察者之间不要形成循环引用,否则可能导致内存泄漏或死循环

不需要接收通知时要及时注销观察者,避免内存泄漏,特别是在Android等有生命周期的系统中

当观察者数量较多或通知操作耗时较长时,可以考虑使用异步通知,避免阻塞主题的正常运行

在多线程环境下使用观察者模式时,要注意线程安全问题,特别是观察者列表的修改和通知操作

避免过度使用观察者模式

http://www.jsqmd.com/news/767826/

相关文章:

  • PE-bear:免费PE文件分析神器,让Windows逆向工程变得简单快速
  • 从0到1掌握反反爬:IP封禁与UA检测的底层原理及工业级突破方案
  • 打破数据黑盒依赖困境,镜像视界开创可信孪生时代
  • 别再为调试器发愁了!手把手教你用OpenOCD搞定J-Link、ST-Link和FTDI
  • 千问 LeetCode 2122.还原原数组 public int[] recoverArray(int[] nums)
  • 移植代码后LED灯都不闪了?可能是VTOR向量表地址在捣鬼(附STM32CubeIDE与Keil排查步骤)
  • Ising机与Bounce-Bind机制在组合优化中的应用
  • 构建可移植开发环境:配置仓库与自动化部署实践
  • 机械操作耗尽精力?dothething:一款全自主本地 AI 代理,替你接管系统控制与网络任务
  • RTX 3050 + Win11实测:Python 3.10环境下,用pip搞定TensorFlow-GPU 2.10.1的完整避坑指南
  • OpencvSharp 算子学习教案之 - Cv2.GetStructuringElement 重载1
  • STM32F103C8T6硬件SPI驱动W25Q64 Flash全流程(附完整工程代码)
  • C#基础(持续更新中)
  • Python初学者项目练习9--对简单列表元素排序
  • 解决ZYNQ裸机网络扩展难题:为LWIP库添加自定义PHY驱动与SDK配置界面
  • Windows系统光标深度替换:INF方案实现macOS指针移植与优化
  • AI编码助手统一配置工具agent-dotfiles:告别重复配置,实现规则与技能一键同步
  • BrowserClaw:基于Puppeteer与Playwright的浏览器自动化与数据抓取实践
  • AI工具搭建自动化视频生成图像缩放
  • ChatGPT文档格式化指令:打造Google Docs无缝协作的AI写作规范
  • GRADFILTERING:基于梯度信噪比的指令调优数据筛选方法
  • 别再死记硬背async/await了!用Playwright+Python写自动化脚本,这3个坑我帮你踩过了
  • 千问 LeetCode 2127.参加会议的最多员工数 public int maximumInvitations(int[] favorite)
  • 解释器模式是行为型设计模式的一种,其核心思想是给定一个语言,定义它的文法的一种表示
  • STM32G431RBT6的HAL库避坑指南:蓝桥杯嵌入式那些CubeMX没告诉你的细节
  • 构建本地化音视频转录分析平台:Whisper+Ollama+Meilisearch实战
  • SolidGPT实战指南:基于语义搜索的代码与文档智能问答系统
  • 避坑指南:SAP固定资产配置里,记账码70和31千万别乱选!附SPRO完整路径
  • 想在Win10任务栏显示秒数?试试用StartAllBack配合注册表修改(附详细步骤)
  • 【Redis】Redis——过期键删除策略、内存淘汰8种策略、LRU/LFU实现