观察者模式(Observer Pattern)详解

文章目录

    • 1. 什么是观察者模式?
    • 2. 为什么需要观察者模式?
    • 3. 观察者模式的核心概念
    • 4. 观察者模式的结构
    • 5. 观察者模式的基本实现
      • 简单的气象站示例
    • 6. 观察者模式的进阶实现
      • 推模型 vs 拉模型
        • 6.1 推模型(Push Model)
        • 6.2 拉模型(Pull Model)
    • 7. 观察者模式的复杂实现
      • 7.1 在线商店库存通知系统
      • 7.2 事件监听系统
    • 8. 观察者模式在Java中的实际应用
      • 8.1 Java原生观察者模式
      • 8.2 JavaBeans的PropertyChangeListener
      • 8.3 Java Swing事件监听模型
    • 9. 观察者模式的优缺点
      • 9.1 优点
      • 9.2 缺点
    • 10. 何时使用观察者模式?
    • 11. 观察者模式与其他设计模式的比较
      • 11.1 观察者模式 vs 发布-订阅模式
      • 11.2 观察者模式 vs 中介者模式
      • 11.3 观察者模式 vs 策略模式
    • 12. 常见问题与回答
      • Q1: 如何避免观察者模式中的循环依赖问题?
      • Q2: 如何处理观察者模式中的内存泄漏问题?
      • Q3: 观察者模式的通知有哪些不同策略?
      • Q4: 观察者模式如何处理线程安全问题?
    • 13. 观察者模式的实现变体
      • 13.1 异步观察者模式
      • 13.2 优先级观察者模式
    • 14. 总结

1. 什么是观察者模式?

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。

观察者模式又被称为发布-订阅(Publish/Subscribe)模式、模型-视图(Model-View)模式或源-监听器(Source-Listener)模式。

2. 为什么需要观察者模式?

在以下情况下,观察者模式特别有用:

  1. 当一个对象的改变需要同时改变其他对象时:观察者模式可以实现这种一对多的通知机制
  2. 当一个对象必须通知其他对象,而它又不需要知道这些对象是谁时:主题只需要知道观察者实现了特定接口
  3. 当你需要维护对象之间的一致性时:不必使各个对象紧密耦合
  4. 当抽象模型有两个方面,其中一个方面依赖于另一个方面时:将这两者封装在独立的对象中允许您分别使用和修改它们

3. 观察者模式的核心概念

观察者模式主要涉及以下角色:

  1. 主题(Subject)

    • 知道它的观察者,可以有任意多个观察者观察同一个主题
    • 提供注册和删除观察者对象的接口
  2. 具体主题(Concrete Subject)

    • 存储观察者感兴趣的状态
    • 当状态发生变化时通知观察者
  3. 观察者(Observer)

    • 为那些在主题状态发生改变时需获得通知的对象定义一个更新接口
  4. 具体观察者(Concrete Observer)

    • 实现观察者更新接口以响应主题状态的变化
    • 保持与主题的一致性

4. 观察者模式的结构

观察者模式的UML类图如下:

+----------------+        +----------------+
| Subject        |<>----->| Observer       |
+----------------+        +----------------+
| attach()       |        | update()       |
| detach()       |        +----------------+
| notify()       |               ^
+----------------+               |^                         ||                         |
+----------------+        +-------------------+
| ConcreteSubject|        | ConcreteObserver  |
+----------------+        +-------------------+
| getState()     |        | update()          |
| setState()     |        | observerState     |
+----------------+        +-------------------+

5. 观察者模式的基本实现

简单的气象站示例

下面是一个简单的气象站示例,展示了观察者模式的基本实现。

首先,定义观察者接口:

// 观察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}

然后,定义主题接口:

// 主题接口
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}

接着,实现具体主题(气象数据):

// 具体主题 - 气象数据
public class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer observer) {if (!observers.contains(observer)) {observers.add(observer);}}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}// 当气象测量数据改变时,通知观察者public void measurementsChanged() {notifyObservers();}// 设置气象测量数据public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}
}

然后,实现具体观察者(显示器):

// 具体观察者 - 当前状况显示
public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("当前状况: 温度 " + temperature + "°C, 湿度 " + humidity + "%");}
}// 具体观察者 - 统计信息显示
public class StatisticsDisplay implements Observer {private float maxTemp = 0.0f;private float minTemp = 200.0f;private float sumTemp = 0.0f;private int countReadings = 0;private Subject weatherData;public StatisticsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {sumTemp += temperature;countReadings++;if (temperature > maxTemp) {maxTemp = temperature;}if (temperature < minTemp) {minTemp = temperature;}display();}public void display() {System.out.println("统计信息: 平均温度 " + (sumTemp / countReadings) + "°C, 最高温度 " + maxTemp + "°C, 最低温度 " + minTemp + "°C");}
}// 具体观察者 - 天气预报显示
public class ForecastDisplay implements Observer {private float currentPressure = 29.92f;private float lastPressure;private Subject weatherData;public ForecastDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {lastPressure = currentPressure;currentPressure = pressure;display();}public void display() {System.out.print("天气预报: ");if (currentPressure > lastPressure) {System.out.println("天气正在好转!");} else if (currentPressure == lastPressure) {System.out.println("天气状况保持不变");} else {System.out.println("天气可能转凉,注意保暖");}}
}

最后,创建一个测试类来演示观察者模式:

public class WeatherStation {public static void main(String[] args) {// 创建主题WeatherData weatherData = new WeatherData();// 创建观察者CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);// 模拟气象数据变化System.out.println("第一次气象数据更新:");weatherData.setMeasurements(25.0f, 65.0f, 30.4f);System.out.println("\n第二次气象数据更新:");weatherData.setMeasurements(27.0f, 70.0f, 29.2f);System.out.println("\n第三次气象数据更新:");weatherData.setMeasurements(20.0f, 90.0f, 29.2f);// 移除一个观察者System.out.println("\n移除统计信息显示后:");weatherData.removeObserver(statisticsDisplay);weatherData.setMeasurements(22.0f, 80.0f, 31.0f);}
}

输出结果:

第一次气象数据更新:
当前状况: 温度 25.0°C, 湿度 65.0%
统计信息: 平均温度 25.0°C, 最高温度 25.0°C, 最低温度 25.0°C
天气预报: 天气状况保持不变第二次气象数据更新:
当前状况: 温度 27.0°C, 湿度 70.0%
统计信息: 平均温度 26.0°C, 最高温度 27.0°C, 最低温度 25.0°C
天气预报: 天气可能转凉,注意保暖第三次气象数据更新:
当前状况: 温度 20.0°C, 湿度 90.0%
统计信息: 平均温度 24.0°C, 最高温度 27.0°C, 最低温度 20.0°C
天气预报: 天气状况保持不变移除统计信息显示后:
当前状况: 温度 22.0°C, 湿度 80.0%
天气预报: 天气正在好转!

6. 观察者模式的进阶实现

推模型 vs 拉模型

观察者模式有两种常见的实现方式:推模型和拉模型。

6.1 推模型(Push Model)

在推模型中,主题对象向观察者推送所有数据,不管观察者是否需要。上面的例子使用的就是推模型。

6.2 拉模型(Pull Model)

在拉模型中,主题仅通知观察者有数据更新,由观察者自己决定获取哪些数据。下面我们将上面的例子修改为拉模型:

首先,修改观察者接口,update方法不再传递数据:

// 观察者接口 - 拉模型
public interface Observer {void update();
}

修改主题接口,添加获取数据的方法:

// 主题接口 - 拉模型
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();// 获取数据的方法float getTemperature();float getHumidity(<

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

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

相关文章

前端代码规范详细配置

以下是现代前端项目的完整代码规范配置方案&#xff0c;涵盖主流技术栈和自动化工具链配置&#xff1a; 一、基础工程配置 1. 项目结构规范 project/ ├── src/ │ ├── assets/ # 静态资源 │ ├── components/ # 通用组件 │ ├── layouts/ …

Missashe考研日记-day34

Missashe考研日记-day34 1 专业课408 学习时间&#xff1a;3h学习内容&#xff1a; 今天是学习I/O管理第二小节的内容&#xff0c;听了课也做了题&#xff0c;这是操作系统倒数第二节知识了&#xff0c;还差最后一节就完结了。知识点回顾&#xff1a; 1.I/O核心子系统&#x…

Milvus 向量数据库详解与实践指南

一、Milvus 核心介绍 1. 什么是 Milvus&#xff1f; Milvus 是一款开源、高性能、可扩展的向量数据库&#xff0c;专门为海量向量数据的存储、索引和检索而设计。它支持近似最近邻搜索&#xff08;ANN&#xff09;&#xff0c;适用于图像检索、自然语言处理&#xff08;NLP&am…

算力经济模型研究:从云计算定价到去中心化算力市场设计

引言&#xff1a;算力商品化的双重革命 在H800 GPU集群的算力供给能力突破2.3 EFLOPS的今天&#xff0c;算力定价机制正经历从"资源租赁"到"动态市场"的范式转变。传统云计算定价模型&#xff08;如AWS按需实例&#xff09;的静态价格机制已难以适应大模型…

[D1,2] 贪心刷题

文章目录 摆动序列最大子数组合买卖股票跳跃游戏跳跃2 摆动序列 不像是贪心&#xff0c;只要抓住摆动这个点&#xff0c;前一个上升&#xff0c;那下一个就要下降&#xff0c;记录上一次的状态为1的话&#xff0c;那下一次就要更新为-1&#xff0c;如果上一次为1&#xff0c;这…

Spring Boot操作MongoDB的完整示例大全

以下是基于Spring Boot操作MongoDB的完整示例大全&#xff0c;涵盖增删改查、聚合查询、索引、事务等核心功能&#xff1a; 一、基础CRUD操作 1. 环境配置 依赖配置&#xff08;pom.xml&#xff09; <dependency><groupId>org.springframework.boot</groupId…

【实战教程】零基础搭建DeepSeek大模型聊天系统 - Spring Boot+React完整开发指南

&#x1f525; 本文详细讲解如何从零搭建一个完整的DeepSeek AI对话系统&#xff0c;包括Spring Boot后端和React前端&#xff0c;适合AI开发入门者快速上手。即使你是编程萌新&#xff0c;也能轻松搭建自己的AI助手&#xff01; &#x1f4da;博主匠心之作&#xff0c;强推专栏…

Linux系统基本指令和知识指南

一、Linux系统简介 Linux是一种自由和开放源代码的类UNIX操作系统&#xff0c;由林纳斯托瓦兹在1991年首次发布。它以稳定性、安全性和灵活性著称&#xff0c;广泛应用于服务器、嵌入式系统和个人计算机。 Linux主要特点&#xff1a; 开源免费 多用户、多任务 良好的安全性…

【计算机视觉】OpenCV实战项目:Long-Exposure:基于深度学习的长时间曝光合成技术

Long-Exposure&#xff1a;基于深度学习的长时间曝光合成技术 项目概述与技术背景项目核心功能技术原理 环境配置与安装硬件要求建议详细安装步骤可选组件安装 实战应用指南1. 基础使用&#xff1a;视频转长曝光2. 高级模式&#xff1a;自定义光轨合成3. 批量处理模式 技术实现…

TikTok 矩阵账号运营实操细节:打造爆款矩阵

在 TikTok 的流量版图里&#xff0c;打造 TikTok 矩阵账号能显著提升影响力与吸粉能力。而借助 AI 工具&#xff0c;更可为 TikTok 矩阵运营效率的提升赋能&#xff0c;让运营如虎添翼。下面就为大家详细讲讲其中的实操细节&#xff0c;并结合一些伪代码示例辅助理解。 一、矩…

互联网大厂Java求职面试:分布式系统中向量数据库与AI应用的融合探索

互联网大厂Java求职面试&#xff1a;分布式系统中向量数据库与AI应用的融合探索 面试开场&#xff1a;技术总监与郑薪苦的“较量” 技术总监&#xff08;以下简称T&#xff09;&#xff1a;郑薪苦先生&#xff0c;请简单介绍一下你在分布式系统设计方面的经验。 郑薪苦&…

【每日八股】学习 RocketMQ Day2:进阶(一)

文章目录 复习昨日内容为什么要使用消息队列为什么选择 RocketMQRocketMQ 的优缺点&#xff1f;谈谈你对 RocketMQ 的理解&#xff1f;消息队列有哪些类型&#xff1f;RocketMQ 采用哪种消息队列模型&#xff1f;消息的消费模式了解吗&#xff1f;了解 RocketMQ 的基本架构吗&a…

探索智能体开发新边界:Cangjie Magic开源平台体验与解析

文章目录 每日一句正能量前言一、Cangjie Magic的核心技术&#xff08;一&#xff09;Agent DSL架构&#xff08;二&#xff09;原生支持MCP通信协议&#xff08;三&#xff09;智能规划功能 二、实际应用场景&#xff08;一&#xff09;智能客服系统&#xff08;二&#xff09…

深入解析进程间通信与Socket原理:从理论到TypeScript实战

文章目录 一、进程中如何通信1.1 管道1.1.1 核心特性1.1.2 缺点1.1.3 匿名管道与命名管道的对比 1.2 信号1.2.1 核心特性1.2.2 缺点1.2.3 信号分类对比 1.3 消息队列1.3.1 核心特性1.3.2 缺点 1.4 共享内存1.4.1 核心特性1.4.2 缺点 1.5 信号量1.5.1 核心特性1.5.2 缺点 二、So…

力扣-hot100(旋转图像)

48. 旋转图像 中等 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4…

Docker编排工具---Compose的概述及使用

目录 一、Compose工具的概述 二、Compose的常用命令 1、列出容器 2、查看访问日志 3、输出绑定的公共端口 4、重新构建服务 5、启动服务 6、停止服务 7、删除已停止服务的容器 8、创建和启动容器 9、在运行的容器中执行命令 10、指定一个服务启动容器的个数 11、其…

C25-数组应用及练习

第一题 题目: 代码 #include <stdio.h> int main() {//数组及相关数据定义int arr[10];int i;//基于循环的数组数据输入for(i0;i<10;i){arr[i]i;}//基于循环的数组数据输出for(i9;i>0;i--){printf("%d ",arr[i]);}return 0; }结果 第二题 题目 代码 …

网络安全怎么入门?快速了解

网络安全是一个快速发展的领域&#xff0c;入门需要系统化的学习和实践。以下是适合零基础或转行者的分阶段学习路径&#xff0c;涵盖必备知识、学习资源、实战方法和职业方向&#xff1a; 一、基础阶段&#xff08;1-3个月&#xff09; 1. 掌握核心基础知识 计算机网络&#…

express 怎么搭建 WebSocket 服务器

一&#xff1a;使用 express-ws var express require(express); var app express(); var expressWs require(express-ws)(app);app.use(function (req, res, next) {console.log(middleware);req.testing testing;return next(); });app.get(/, function(req, res, next){…

【AI论文】SuperEdit:修正并促进基于指令的图像编辑的监督信号

摘要&#xff1a;由于手动收集准确的编辑数据存在挑战&#xff0c;现有的数据集通常使用各种自动化方法构建&#xff0c;导致编辑指令和原始编辑图像对之间不匹配导致监督信号出现噪声。 最近的研究试图通过生成更高质量的编辑图像、在识别任务上进行预训练或引入视觉语言模型&…