设计模式——观察者设计模式(行为型)

摘要

本文详细介绍了观察者设计模式,包括其定义、结构、实现方式、适用场景以及实战示例。通过代码示例展示了如何在Spring框架下实现观察者模式,以及如何通过该模式实现状态变化通知。同时,对比了观察者模式与消息中间件在设计理念、耦合程度、通信方式和分布式支持等方面的差异,帮助读者更好地理解和选择合适的实现方式。

1. 观察者设计模式定义

观察者设计模式(Observer Pattern)是一种行为型设计模式,定义了一种一对多的依赖关系,使得当一个对象(被观察者)的状态发生改变时,所有依赖它的对象(观察者)都会得到自动通知并更新,从而实现对象间的松耦合和实时通讯。观察者模式通过定义对象间的发布-订阅关系,实现事件的自动通知和响应,适合事件驱动和异步消息场景。

1.1. 核心定义

角色

  • 被观察者(Subject):维护一组观察者,负责状态的管理和通知。
  • 观察者(Observer):注册到被观察者,一旦被观察者状态变化,接收通知并执行相应动作。

目的:让多个观察者对象自动获得被观察者的状态变化,降低对象间耦合度。

2. 观察者设计模式结构

观察者模式包含如下角色:

  • Subject: 目标
  • ConcreteSubject: 具体目标
  • Observer: 观察者
  • ConcreteObserver: 具体观察者

2.1. 观察者模式类图

2.2. 观察者模式时序图

3. 观察者设计模式实现方式

3.1. 观察者设计模式实现步骤

3.1.1. 定义抽象主题(Subject)接口

  • 提供注册、移除和通知观察者的方法。
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}

3.1.2. 定义抽象观察者(Observer)接口

  • 声明一个更新方法,主题状态变化时调用。
public interface Observer {void update(String message);
}

3.1.3. 具体主题类实现 Subject

  • 维护观察者列表,实现注册、移除、通知逻辑。
  • 当自身状态变化时调用 notifyObservers(),遍历通知所有观察者。
import java.util.ArrayList;
import java.util.List;public class ConcreteSubject implements Subject {private final List<Observer> observers = new ArrayList<>();private String state;@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(state);}}public void setState(String state) {this.state = state;notifyObservers();}
}

3.1.4. 具体观察者实现 Observer

  • 实现 update 方法,接收通知并做出响应。
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " 收到通知,状态变为: " + message);}
}

3.2. 观察者测试示例

public class ObserverPatternDemo {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("观察者1");Observer observer2 = new ConcreteObserver("观察者2");subject.registerObserver(observer1);subject.registerObserver(observer2);subject.setState("状态1");subject.setState("状态2");}
}

说明

  • 主题(Subject)维护观察者集合,状态变化时通知所有观察者。
  • 观察者实现更新接口,获得主题最新状态。

4. 观察者设计模式适合场景

4.1. ✅ 适合使用观察者设计模式的场景

适合场景

说明

事件驱动系统

事件产生后需要通知多个模块或对象做出响应,如 UI 事件监听、消息推送。

一对多依赖关系

一个对象状态变化需要通知多个依赖对象,如股票价格更新通知所有订阅者。

松耦合需求

希望对象间解耦,观察者与被观察者不直接依赖,实现灵活扩展。

广播通信

需要将消息广播给多个接收者,且接收者可动态增加和移除。

异步通知

状态更新后,通知观察者进行异步处理。

分布式系统

多个服务或模块之间的状态同步与消息推送。

4.2. ❌ 不适合使用观察者设计模式的场景

不适合场景

说明

对象之间不存在依赖关系

彼此独立的对象无需相互通知。

通知对象数量固定且简单

只有少数固定对象,且耦合关系明确,使用简单调用即可。

需要严格同步控制的场景

观察者通知是异步的,无法满足严格同步时序需求。

性能敏感场景

大量观察者通知导致性能开销大,影响系统响应速度。

过度使用导致复杂性增加

观察者链条过长,维护困难,代码难以理解。

总结:观察者模式主要适用于需要“自动广播状态变化给多个对象”且“希望对象间低耦合”的场景。不适合简单调用、同步严格或性能瓶颈明显的场景。

5. 观察者设计模式实战示例

5.1. 场景说明

在风控系统中,当订单状态发生变化(比如风控审核结果出来后),需要通知多个模块(比如日志记录模块、报警模块、缓存更新模块)来响应该状态变化。

5.2. 定义观察者接口

public interface OrderStatusObserver {void update(String orderId, String status);
}

5.3. 定义具体观察者实现类

import org.springframework.stereotype.Component;@Component
public class LoggingObserver implements OrderStatusObserver {@Overridepublic void update(String orderId, String status) {System.out.println("日志模块:订单 " + orderId + " 状态更新为:" + status);// 这里可以写日志持久化操作}
}@Component
public class AlertObserver implements OrderStatusObserver {@Overridepublic void update(String orderId, String status) {if ("REJECTED".equals(status)) {System.out.println("报警模块:订单 " + orderId + " 审核拒绝,触发报警!");// 触发报警逻辑}}
}@Component
public class CacheObserver implements OrderStatusObserver {@Overridepublic void update(String orderId, String status) {System.out.println("缓存模块:更新订单 " + orderId + " 状态缓存为:" + status);// 缓存刷新逻辑}
}

5.4. 定义主题接口和实现类

public interface OrderStatusSubject {void registerObserver(OrderStatusObserver observer);void removeObserver(OrderStatusObserver observer);void notifyObservers(String orderId, String status);
}
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;@Component
public class OrderStatusSubjectImpl implements OrderStatusSubject {private final List<OrderStatusObserver> observers = new ArrayList<>();// 使用Spring自动注入所有实现了OrderStatusObserver接口的Bean@Autowiredprivate List<OrderStatusObserver> observerBeans;@PostConstructpublic void init() {// 初始化时,将所有观察者注册进列表observers.addAll(observerBeans);}@Overridepublic void registerObserver(OrderStatusObserver observer) {observers.add(observer);}@Overridepublic void removeObserver(OrderStatusObserver observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String orderId, String status) {for (OrderStatusObserver observer : observers) {observer.update(orderId, status);}}
}

5.5. 4. 业务调用示例(比如风控服务)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class RiskControlService {@Autowiredprivate OrderStatusSubject orderStatusSubject;public void auditOrder(String orderId, boolean passed) {// 风控业务逻辑String status = passed ? "APPROVED" : "REJECTED";System.out.println("风控审核完成,订单:" + orderId + ",状态:" + status);// 审核完成后,通知所有观察者orderStatusSubject.notifyObservers(orderId, status);}
}

5.6. 测试Controller(可选)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RiskController {@Autowiredprivate RiskControlService riskControlService;@PostMapping("/auditOrder")public String auditOrder(@RequestParam String orderId, @RequestParam boolean passed) {riskControlService.auditOrder(orderId, passed);return "审核完成";}
}

5.7. 实战示例总结

  • 观察者接口 OrderStatusObserver,多个实现类由Spring管理并注入。
  • 主题 OrderStatusSubjectImpl 维护观察者列表,利用 @Autowired 自动注入所有观察者Bean。
  • 风控业务执行后调用主题的 notifyObservers(),实现状态变化通知。
  • 全部采用Spring注解管理,未用构造函数注入,代码简洁且易于扩展。

6. 观察者设计模式思考

6.1. 观察着模式和使用消息中间件来通知处理的方式有什么不同?

很好的问题!观察者模式和消息中间件在实现“发布-订阅”或通知机制时都很常用,但它们的设计理念、实现方式和适用场景有显著差别。下面帮你详细分析两者的区别:

维度

观察者模式

消息中间件(如Kafka、RabbitMQ、ActiveMQ等)

设计层次

设计模式,属于应用内部的设计范式,通常是在代码内部直接实现。

独立的中间件系统,属于基础设施层,提供异步消息传递机制。

耦合程度

被观察者和观察者耦合较紧密,观察者必须知道被观察者实例(尽管解耦设计可以降低耦合)。

发布者和订阅者完全解耦,彼此不直接依赖,只通过消息中间件通信。

通信方式

通常是同步调用,状态变化时立即调用观察者的更新方法。

典型异步通信,消息发送后,接收方异步消费,支持异步处理和缓冲。

分布式支持

一般用于单体应用或同一JVM内对象之间的通知。

天生支持分布式系统,跨进程、跨服务器,甚至跨数据中心的消息传递。

扩展性

观察者数量有限,扩展时需要修改代码或依赖注入配置。

高度扩展,支持大量消费者和复杂路由规则,方便动态添加消费者。

容错与持久化

无持久化和容错机制,一旦通知失败可能丢失信息。

支持消息持久化、重试、消息确认、死信队列,增强系统可靠性。

性能影响

观察者调用是同步的,影响调用链的执行时间,可能导致性能瓶颈。

异步处理,解耦主业务流程,提升系统吞吐量和响应速度。

使用场景

小型系统、UI事件响应、简单状态同步、模块内部事件通知。

大型分布式系统、微服务间异步通信、异步任务调度、事件驱动架构。

6.1.1. 简单总结:

  • 观察者模式 适合应用内、轻量级的同步通知,设计简单,适合对象之间状态变化通知。
  • 消息中间件 适合跨进程、跨网络、异步且高可靠的消息通信,适合分布式系统异步解耦和复杂事件流转。

6.1.2. 举例对比:

  • 观察者模式:风控模块风控结果出来后,同步通知日志模块和报警模块,立即处理。
  • 消息中间件:风控模块发布“风控结果”消息到消息队列,日志模块和报警模块异步订阅该消息,消息队列保证消息可靠送达和解耦。

博文参考

  • 3. 观察者模式 — Graphic Design Patterns
  • 观察者设计模式

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

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

相关文章

uniapp 页面栈一定深度后,回首页导航到新页面的解决方案

uniapp 页面栈一定深度后&#xff0c;回首页导航到新页面的解决方案 uniapp 页面导航解决方案 在 uniapp 中&#xff0c;要实现先弹出页面栈回到首页&#xff0c;然后再跳转到指定页面。 /*** description 后台选择链接专用跳转*/ interface Link {path: string;name?: stri…

数据结构 散列表 学习 2025年6月12日15:30:48

数据结构 散列表 哈希表(Hash Table): 通过哈希函数将键&#xff08;key&#xff09;映射到存储位置&#xff0c;从而实现快速的插入、删除和查找操作。 哈希表是现代编程中最重要的数据结构之一&#xff0c;几乎所有编程语言都提供了内置实现。 计数 #include <stdio.h&g…

数据结构之LinkedList

系列文章目录 数据结构之ArrayList-CSDN博客 目录 系列文章目录 前言 一、模拟实现链表 1. 遍历链表 2. 插入节点 3. 删除节点 4. 清空链表 二、链表的常见操作 1. 反转链表 2. 返回链表的中间节点 3. 链表倒数第 k 个节点 4. 合并两个有序链表 5. 分割链表 6. 判…

DC3靶机渗透

1. 靶机介绍 主要的内容有 sql 注入漏洞、joomla 框架漏洞、ssh 攻击、shell 反弹、提权 信息收集(ip、端口、目录、指纹信息)--->利用漏洞--->反弹---->提权 2. 信息收集 2.1. 扫描存活 ip 192.168.220.134 2.2. 端口扫描 nmap -T4 -A -p- 192.168.220.134 …

C# 线程交互

一、为什么要进行线程交互 在C#中&#xff0c;线程交互通常涉及到多个线程之间的数据共享和同步。‌. 一、全局变量 在C#中&#xff0c;全局变量是指在程序的任何地方都可以访问的变量。通常&#xff0c;全局变量是在类的外部定义的&#xff0c;或者在所有方法之外定义的。全…

Cursor 编辑器中的 Notepad 功能使用指南

Cursor 编辑器中的 Notepad 功能使用指南 摘要 本指南全面介绍了 Cursor 编辑器中的 Notepad 功能&#xff0c;涵盖其用途、多种访问方式、适用场景以及与其它功能的整合技巧等内容&#xff0c;助力用户高效利用该功能提升工作流程效率。 不同访问方式介绍 功能概述 Curso…

用于评估大语言模型(LLMs)能力的重要基准任务(Benchmark)

基准任务涵盖了 多领域&#xff08;如语言理解、数学、推理、编程、医学等&#xff09;和 多能力维度&#xff08;如事实检索、计算、代码生成、链式推理、多语言处理&#xff09;。常用于模型发布时的对比评测&#xff0c;例如 GPT-4、Claude、Gemini、Mistral 等模型的论文或…

力扣HOT100之技巧:169. 多数元素

这道题如果不考虑空间复杂度和时间复杂度的限制的话很好做&#xff0c;一种思路是通过一次遍历将所有元素的数量记录在一个哈希表中&#xff0c;然后我们直接返回出现次数最多的键即可。另一种思路是直接对数组进行排序&#xff0c;数组中间的值一定是多数元素&#xff0c;因为…

wordpress首页调用指定ID页面内的相册

要在WordPress首页调用ID为2的页面中的相册&#xff0c;你可以使用以下几种方法&#xff1a; 方法一&#xff1a;使用短代码和自定义查询 首先&#xff0c;在你的主题的functions.php文件中添加以下代码&#xff1a; function display_page_gallery($atts) {$atts shortcod…

基于深度学习的异常检测系统:原理、实现与应用

前言 在现代数据驱动的业务环境中&#xff0c;异常检测&#xff08;Anomaly Detection&#xff09;是一个关键任务&#xff0c;它能够帮助企业和组织及时发现数据中的异常行为或事件&#xff0c;从而采取相应的措施。异常检测广泛应用于金融欺诈检测、网络安全、工业设备故障监…

Java基于BS架构的OA流程可视化实战:从工作流引擎到前端交互(附完整源代码+论文框架)

一、引言&#xff1a;BS架构OA系统的流程可视化需求 在企业信息化建设中&#xff0c;基于浏览器/服务器&#xff08;BS&#xff09;架构的OA系统通过流程自动化提升办公效率&#xff0c;而流程可视化是实现流程监控、优化的核心模块。本文基于Java技术栈&#xff0c;结合Activ…

JavaWeb-数据库连接池

目录 1.springboot默认Hikari(追光者)连接池 2.切换为Druid(德鲁伊)连接池 1.springboot默认Hikari(追光者)连接池 2.切换为Druid(德鲁伊)连接池 一般几乎用不到&#xff0c;不需要切换 <!--Druid连接池--> <dependency><groupId>com.alibaba</groupId&…

c# 完成恩尼格玛加密扩展

c# 完成恩尼格玛加密扩展 恩尼格玛扩展为可见字符恩尼格玛的设备原始字符顺序转子的设置反射器的设置连接板的设置 初始数据的设置第一版 C# 代码第二版 C# 代码 总结 恩尼格玛 在之前&#xff0c;我们使用 python 实现了一版恩尼格玛的加密算法&#xff0c;但是这一版&#x…

【Redisson】锁可重入原理

目录 一、基本原理 二、源码解析&#xff1a; &#xff08;2&#xff09;获取锁 &#xff08;1&#xff09;释放锁&#xff1a; 之前给大家介绍过redisson的分布式锁&#xff0c;用redisson来实现比自己手搓简单的分布式锁有很多好处&#xff0c;因为这些可重入、可重试的逻…

BERT 模型微调与传统机器学习的对比

BERT 微调与传统机器学习的区别和联系&#xff1a; 传统机器学习流程 传统机器学习处理文本分类通常包含以下步骤&#xff1a; 特征工程&#xff1a;手动设计特征&#xff08;如 TF-IDF、词袋模型&#xff09;模型训练&#xff1a;使用分类器&#xff08;如 SVM、随机森林、逻…

(12)-Fiddler抓包-Fiddler设置IOS手机抓包

1.简介 Fiddler不但能截获各种浏览器发出的 HTTP 请求&#xff0c;也可以截获各种智能手机发出的HTTP/ HTTPS 请求。 Fiddler 能捕获Android 和 Windows Phone 等设备发出的 HTTP/HTTPS 请求。同理也可以截获iOS设备发出的请求&#xff0c;比如 iPhone、iPad 和 MacBook 等苹…

芯科科技Tech Talks技术培训重磅回归:赋能物联网创新,共筑智能互联未来

聚焦于Matter、蓝牙、Wi-Fi、LPWAN、AI/ML五大热门无线协议与技术 为年度盛会Works With大会赋能先行 随着物联网&#xff08;IoT&#xff09;和人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;越来越多的企业和个人开发者都非常关注最新的无线连接技术和应用…

docker-compose容器单机编排

docker-compose容器单机编排 开篇前言 随着网站架构的升级&#xff0c;容器的使用也越来越频繁&#xff0c;应用服务和容器之间的关系也越发的复杂。 这个就要求研发人员能更好的方法去管理数量较多的服务器&#xff0c;而不能手动挨个管理。 例如一个LNMP 架构&#xff0c;就…

LeetCode--29.两数相除

解题思路&#xff1a; 1.获取信息&#xff1a; 给定两个整数&#xff0c;一个除数&#xff0c;一个被除数&#xff0c;要求返回商&#xff08;商取整数&#xff09; 限制条件&#xff1a;&#xff08;1&#xff09;不能使用乘法&#xff0c;除法和取余运算 &#xff08;2&#…

中山大学GaussianFusion:首个将高斯表示引入端到端自动驾驶多传感器融合的新框架

摘要 近年来由于端到端自动驾驶极大简化了原有传统自动驾驶模块化的流程&#xff0c;吸引了来自工业界和学术界的广泛关注。然而&#xff0c;现有的端到端智驾算法通常采用单一传感器&#xff0c;使其在处理复杂多样和具有挑战性的驾驶场景中受到了限制。而多传感器融合可以很…