观察者模式全攻略:从设计原理到 SpringBoot 实践案例
观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都能得到通知并自动更新。
核心思想:
观察者模式将**观察者(Observer)和被观察者(Subject)**解耦,允许对象独立变化,并且当被观察者状态变化时,自动通知所有观察者。
观察者模式中的角色:
- Subject(被观察者):负责维护观察者列表,并在自身状态发生变化时通知所有观察者。它提供注册、移除和通知观察者的方法。
- Observer(观察者):定义一个接口,用于接收来自被观察者的通知。
- ConcreteSubject(具体被观察者):实现
Subject
接口,包含具体的状态,并在状态变化时调用notifyObservers()
。 - ConcreteObserver(具体观察者):实现
Observer
接口,并在被观察者状态变化时更新自身。
观察者模式的 UML 原理类图
类图解释:
- Subject(被观察者接口):
- 定义了方法
registerObserver()
、removeObserver()
和notifyObservers()
,用于管理观察者的注册、移除和通知操作。
- 定义了方法
- Observer(观察者接口):
- 定义了
update()
方法,观察者通过该方法接收来自被观察者的更新通知。
- 定义了
- ConcreteSubject(具体被观察者):
- 维护一个观察者列表,并保存自身的状态。当状态发生变化时,调用
notifyObservers()
方法通知所有观察者。 - 提供了
getState()
和setState()
方法来管理其状态,并在状态更新时调用通知方法。
- 维护一个观察者列表,并保存自身的状态。当状态发生变化时,调用
- ConcreteObserver(具体观察者):
- 持有对具体被观察者的引用,并实现
update()
方法,在被观察者状态变化时更新自身。
- 持有对具体被观察者的引用,并实现
案例:天气预报系统 ☀️🌧️
场景说明:
假设我们有一个天气预报系统,它实时监控天气数据,并向多个显示设备(如手机、电视、平板等)推送更新信息。系统中,气象站负责监控天气数据,当气温、湿度等信息发生变化时,它会通知所有的显示设备。这些设备会自动更新并显示最新的天气信息。
代码实现
Step 1: 定义 Observer
接口
// 观察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}
Step 2: 定义 Subject
接口
// 被观察者接口
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}
Step 3: 实现具体的 Subject
类
import java.util.ArrayList;
import java.util.List;// 具体的被观察者:天气数据
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) {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 setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;notifyObservers(); // 通知观察者}
}
Step 4: 实现具体的 Observer
类
PhoneDisplay 观察者
// 具体观察者:手机显示器
public class PhoneDisplay implements Observer {private float temperature;private float humidity;private float pressure;@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;display();}public void display() {System.out.println("Phone Display - Temp: " + temperature + "°C, Humidity: " + humidity + "%, Pressure: " + pressure + "Pa");}
}
TVDisplay 观察者
// 具体观察者:电视显示器
public class TVDisplay implements Observer {private float temperature;private float humidity;private float pressure;@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;display();}public void display() {System.out.println("TV Display - Temp: " + temperature + "°C, Humidity: " + humidity + "%, Pressure: " + pressure + "Pa");}
}
Step 5: 测试观察者模式
public class ObserverPatternDemo {public static void main(String[] args) {// 创建被观察者(天气数据)WeatherData weatherData = new WeatherData();// 创建观察者PhoneDisplay phoneDisplay = new PhoneDisplay();TVDisplay tvDisplay = new TVDisplay();// 注册观察者weatherData.registerObserver(phoneDisplay);weatherData.registerObserver(tvDisplay);// 模拟天气数据变化weatherData.setMeasurements(25.0f, 65.0f, 1013.0f);weatherData.setMeasurements(28.0f, 70.0f, 1010.0f);}
}
输出结果:
Phone Display - Temp: 25.0°C, Humidity: 65.0%, Pressure: 1013.0Pa
TV Display - Temp: 25.0°C, Humidity: 65.0%, Pressure: 1013.0Pa
Phone Display - Temp: 28.0°C, Humidity: 70.0%, Pressure: 1010.0Pa
TV Display - Temp: 28.0°C, Humidity: 70.0%, Pressure: 1010.0Pa
Spring Boot 中使用观察者模式
在 Spring Boot 框架中,观察者模式主要可以通过 Spring 的事件驱动机制(Event Driven) 来实现。这是 Spring 框架对观察者模式的原生支持,允许不同的组件之间进行解耦通信,使得事件的发布者和监听者(观察者)可以独立变化。
Spring 事件驱动机制的核心概念:
- 事件(Event):类似于观察者模式中的 “Subject”(被观察者),当事件发生时,发布该事件。
- 事件监听器(Listener):类似于观察者,监听特定的事件并在事件发布时自动响应。
- 事件发布器(Publisher):发布事件,让所有监听器接收事件通知。
如何在 Spring Boot 中实现观察者模式:步骤详解**
Step 1: 创建自定义事件类
自定义事件类需要继承 Spring 提供的 ApplicationEvent
类。事件类可以封装一些需要传递的状态信息。
import org.springframework.context.ApplicationEvent;// 自定义事件类,继承 ApplicationEvent
public class CustomEvent extends ApplicationEvent {private String message;public CustomEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}
Step 2: 创建事件监听器
实现 ApplicationListener
接口,或使用注解 @EventListener
来创建监听器。监听器会在事件发布时自动被调用。
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;// 事件监听器
@Component
public class CustomEventListener {// 使用 @EventListener 注解监听自定义事件@EventListenerpublic void handleCustomEvent(CustomEvent event) {System.out.println("Received event - " + event.getMessage());}
}
Step 3: 创建事件发布器
使用 ApplicationEventPublisher
来发布事件。当需要通知观察者时,可以发布自定义事件。
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;// 事件发布器
@Component
public class CustomEventPublisher {private final ApplicationEventPublisher publisher;public CustomEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}// 发布事件public void publishEvent(String message) {CustomEvent event = new CustomEvent(this, message);publisher.publishEvent(event); // 发布自定义事件}
}
Step 4: 使用事件发布器
在 Spring Boot 应用中,通过事件发布器发布事件,触发所有监听该事件的观察者。
@RestController
public class BasicController {@PostMapping("/publish")void publish(@RequestBody String message){eventPublisher.publishEvent(message);}}
输出结果
Received event - {"message": "hello"
}
Spring 事件驱动机制 VS 传统观察者模式
相似性:
- 解耦:事件发布者(
ApplicationEventPublisher
)和事件监听器(ApplicationListener
或@EventListener
)之间是解耦的,它们通过事件进行通信。 - 一对多:一个事件可以被多个监听器同时监听,符合观察者模式的一对多依赖关系。
不同点:
- 事件发布是异步的:Spring 的事件驱动机制允许事件的异步处理(如果配置了
@Async
注解),而传统的观察者模式通常是同步执行的。 - 更加灵活的监听机制:Spring 提供了基于注解的
@EventListener
,简化了监听器的注册过程,并可以使用条件过滤等高级功能。
在 Spring Boot 中,通过其内置的 事件驱动机制,可以轻松实现观察者模式。这种机制不仅保留了观察者模式的基本思想,还进一步增强了可扩展性和灵活性,支持异步处理、注解配置等高级特性。它在处理系统之间的解耦和事件驱动编程时尤其强大,非常适合使用在实际项目中。
使用观察者模式的典型场景:
- 异步事件通知:如用户注册、邮件发送等需要异步处理的操作。
- 解耦组件间的通信:模块化应用中,各模块之间的消息传递和事件处理。
总结
观察者模式 提供了一种解耦对象之间的依赖关系的方式,使得被观察者和观察者可以在不直接相互依赖的情况下进行通信。通过观察者模式,当一个对象发生变化时,所有依赖于它的对象都会自动收到通知并作出相应的处理。
相关文章:

观察者模式全攻略:从设计原理到 SpringBoot 实践案例
观察者模式 观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都能得到通知并自动更新。 核心思想: 观察者模式将**观…...
【MyBatis】Java 数据持久层框架:认识 MyBatis
Java 数据持久层框架:认识 MyBatis 1.CRUD 注解2.映射注解3.高级注解3.1 高级注解3.2 MyBatis 3 注解的用法举例 MyBatis 和 JPA 一样,也是一款优秀的 持久层框架,它支持定制化 SQL、存储过程,以及高级映射。它可以使用简单的 XML…...

【Delphi】通过 LiveBindings Designer 链接控件示例
本教程展示了如何使用 LiveBindings Designer 可视化地创建控件之间的 LiveBindings,以便创建只需很少或无需源代码的应用程序。 在本教程中,您将创建一个高清多设备应用程序,该应用程序使用 LiveBindings 绑定多个对象,以更改圆…...

深度学习——基础知识
深度学习的重点在于优化,其中很重要的步骤在于如何调参,会涉及到一些微积分等数学知识。不同于以往接触到的数值运算,深度(机器)学习都是关于张量Tensor(向量)的计算,Python中最常用…...

QT实现升级进度条页面
一.功能说明 在Qt中实现固件升级的进度条显示窗口,你可以通过创建一个自定义的对话框(Dialog)来完成。这个对话框可以包含一个进度条(QProgressBar)、一些文本标签(QLabel)用于显示状态信息&am…...

JavaWeb--纯小白笔记04:Tomcat整合IDEA
IDEA整合Tomcat 1.点击Idea的导航栏里的Run,选择Edit Configurations 2.点击左上角的"",向下翻找到Tomcat Server 选择里面的Local 3.创建一个web工程,点击IDEA的File-->new-->project 然后选择Java Enterprise,…...
【jvm】动态链接为什么需要常量池
目录 1. 常量池的作用2. 动态链接与常量池的关系3. 动态链接的必要性 1. 常量池的作用 1.常量池是JVM(Java虚拟机)中用于存储字面量(如字符串常量、整数常量等)和符号引用(如类和接口的完全限定名、字段的名称和描述符…...
HTTPS详解
文章目录 HTTPS加密 常见加密方式对称加密非对称加密非对称对称数据指纹 证书CA认证数字签名非对称证书对称 中间人 HTTPS 这也是一个应用层协议,是在HTTP协议的基础上引入了一个加密层 为什么要加密呢,这主要是因为如果不对传输主体加密,当…...
redis作为mybaits(mybatisplus)的缓存
引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>配置application.yml文件 spring:data:redis:# 地址host: 127.0.0.1# 端口port: 6379# 数据…...

【环境配置】AST: Asymmetric Student-Teacher Networks for Industrial Anomaly Detection
文章目录 一、环境的配置二、预处理三、训练四、问题 一、环境的配置 # zheP04_cmp_AST $ conda create -n P04_cmp_AST python3.9 $ conda activate P04_cmp_AST $ conda install -y anaconda::scikit-learn $ conda install -y conda-forge::scipy $ conda install -y conda…...

TinkerTool System for Mac实用软件系统维护工具
TinkerTool System 是一款功能全面且强大的 Mac 实用软件,具有以下特点和功能: 软件下载地址 维护功能: 磁盘清理:能够快速扫描并清理系统中的垃圾文件、临时文件以及其他无用文件,释放宝贵的磁盘空间,保…...

物理学基础精解【9】
文章目录 直线与二元一次方程两直线夹角直线方程斜率两点式方程截距式方程将不同形式的直线方程转换为截距方程直线的一般方程直线一般方程的系数有一个或两个为零的直线 参考文献 直线与二元一次方程 两直线夹角 两直线 y 1 k 1 x b 1 , y 2 k 2 x b 2 形成夹角 a 1 和 a…...

Flask-JWT-Extended登录验证
1. 介绍 """安装:pip install Flask-JWT-Extended创建对象 初始化与app绑定jwt JWTManager(app) # 初始化JWTManager设置 Cookie 的选项:除了设置 cookie 的名称和值之外,你还可以指定其他的选项,例如:过期时间 (max_age)&…...

Altium Designer(AD)百度云下载与安装(附安装步骤)
在我们日常使用当中,Altium designer常常也被简称为AD,是一款一体化的电子产品开发系统软件,主要运行在Windows操作系统上。 我们通过Altium designer把原理图设计、电路仿真、PCB绘制编辑、拓扑逻辑自动布线、信号完整性分析和设计输出等技…...

无人机视角下的车辆数据集
车辆数据集 无人机视角下的车辆数据集。数据集为无人机俯拍的真实场景下的车辆机动车数据集。数据集已经标注好,yolo格式,txt标签。数据集已经划分好训练集(20970张图片)验证集(5242张图片)测试集ÿ…...

【MYSQL】聚合查询、分组查询、联合查询
目录 聚合查询聚合函数count()sum()avg()max()和min()总结 分组查询group by 子句having 子句 联合查询笛卡尔积内连接外连接自连接子查询单行子查询多行子查询from子句使用子查询 合并查询 聚合查询 聚合查询就是针对表中行与行之间的查询。 聚合函数 count() count(列名)&a…...

使用IDA Pro动态调试Android APP
版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/ 关于 android_server android_server 是 IDA Pro 在 Android 设备上运行的一个调试服务器。 通过在 Android 设备上运行android_server,IDA Pro …...
JS中的for...in和for...of有什么区别?
你好,我是沐爸,欢迎点赞、收藏、评论和关注。 在 JavaScript 中,for...in 和 for...of 是两种用于遍历数组(或其他可迭代对象)的循环语句,但它们之间存在显著的差异。 一、遍历数组 for…in const arr …...

【C++篇】引领C++模板初体验:泛型编程的力量与妙用
文章目录 C模板编程前言第一章: 初始模板与函数模版1.1 什么是泛型编程?1.1.1 为什么要有泛型编程?1.1.1 泛型编程的优势 1.2 函数模板的基础1.2.1 什么是函数模板?1.2.2 函数模板的定义格式1.2.3 示例:通用的交换函数输出示例&am…...
在react中 使用redux
1.安装redux npm install reduxjs/toolkit react-redux 2.创建切片模块化数据 在Src目录下创建store目录,创建moude目录 创建tab.js import { createSlice } from reduxjs/toolkit; const tabSlice createSlice({name: tab,initialState: {Collapse: false,},re…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...

Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...
拟合问题处理
在机器学习中,核心任务通常围绕模型训练和性能提升展开,但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正: 一、机器学习的核心任务框架 机…...