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

article/2025/8/6 2:02:14

摘要

本文详细介绍了观察者设计模式,包括其定义、结构、实现方式、适用场景以及实战示例。通过代码示例展示了如何在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.hkcw.cn/article/hUGLimWDPL.shtml

相关文章

Android基础入门:dataBinding的简单使用

1.2修改布局文件 选中布局文件的第一行&#xff0c;按alterenter就会弹出提示&#xff0c;默认选中data binding layout 改造好的的新的布局文件里最大的变化就是多了一对<data></data>标签&#xff1b;很容易想到这是为了实现布局文件里数据和布局的分离&#xff…

在Android设置界面中实现颜色选择器

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;在Android开发中&#xff0c;颜色选择器是设置界面中实现用户自定义界面主题色或字体颜色的常用功能。本教程详细介绍了如何设计颜色选择器布局&#xff0c;通过 GridView 或 RecyclerView 展示颜色列表&am…

小程序微信认证/年审流程

看清楚文字描述&#xff0c;别光看图&#xff0c;图并不一定准确&#xff0c;按照你想填写的填写、&#xff0c;本教程只是提供一个参考。 1.登录微信公众平台 : 微信公众平台【← ←点击这个蓝色字体】&#xff08;选择正确的小程序&#xff09;&#xff0c;或者根据第三方…

macOS版本微信 4.0 之后,双开策略

从 macOS 版本微信 3.0 升级到 4.0 之后&#xff0c;之前的双开策略实效了。 当然期待微信能够让之前方法回归。这是最理想的。 咱也该自己动手丰衣足食 &#xff5e;&#xff5e;&#xff5e; 第一步 创建微信的「分身」 sudo cp -R /Applications/WeChat.app /Applicati…

EasyPlayer-RTSP-Android:一款强大的流媒体播放器

EasyPlayer-RTSP-Android&#xff1a;一款强大的流媒体播放器 【下载地址】EasyPlayer-RTSP-Android一款强大的流媒体播放器 EasyPlayer-RTSP-Android 是一款功能强大的 Android 流媒体播放器&#xff0c;支持 RTSP、RTMP、HLS 和 HTTP 等多种协议&#xff0c;适用于各种音视频…

mac怎么安装pycharm?

安装步骤&#xff1a;1、打开PyCharm官网&#xff0c;在官网首页点击“下载”按钮&#xff0c;选择“MacOS”版本进行下载&#xff1b;2、双击打开安装包&#xff0c;将PyCharm拖动到应用程序文件夹中&#xff1b;3、根据提示进行安装&#xff0c;在第一次运行PyCharm时&#x…

【工具】Raycast – Mac提效工具

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 引入 以前看到同事们锁屏的时候,不知按了什么键,直接调出这个框,然后输入lock屏幕就锁了。 跟我习惯的按Mac开机键不大一样。个人觉得还…

【实测可用】Sublime Text4 4169 mac/windows 破解注册 20240417 实测可用

官网下载Sublime Text4 官网地址&#xff1a;https://www.sublimetext.com/ 点击下载即可 不用安装&#xff0c;可以直接使用。 MAC 破解注册 修改可执行文件sublime_text 打开网站https://hexed.it/。找到sublime所在目录&#xff0c;比如我这里是&#xff1a;/Users/xxx/s…

小程序快速实现大模型聊天机器人

需求分析&#xff1a; 基于大模型&#xff0c;打造一个聊天机器人&#xff1b;使用开放API快速搭建&#xff0c;例如&#xff1a;讯飞星火&#xff1b;先实现UI展示&#xff0c;在接入API。 最终实现效果如下&#xff1a; 一.聊天机器人UI部分 1. 创建微信小程序&#xff0c…

iOS全能签使用全攻略

适用系统&#xff1a;iOS 12及以上设备 全能签是一款免费免越狱的IPA签名工具&#xff0c; 支持一键签名、多开安装、插件注入等功能&#xff0c;无需联网即可使用。 哈士奇软件源&#xff08;解锁更多资源&#xff09;&#xff1a;https://yuan.ioska.cn/appstore &#xff0…

手把手教你在VMware虚拟机安装macOS(含避坑指南)

文章目录 ▍前期准备&#xff08;重要&#xff01;&#xff01;&#xff01;&#xff09;必备三件套&#xff1a;避坑提醒&#xff1a; ▍详细安装步骤步骤1&#xff1a;安装Unlocker补丁步骤2&#xff1a;创建虚拟机步骤3&#xff1a;修改虚拟机配置文件步骤4&#xff1a;安装…

mac intel芯片下载安卓模拟器

一、调研 目前主流两个模拟器&#xff1a; 雷神模拟器 不支持macosmumu模拟器pro版 不支持macos intel芯片 搜索到mumu的Q&A中有 “Intel芯片Mac如何安装MuMu&#xff1f;” q&a&#x1f517;&#xff1a;https://mumu.163.com/mac/faq/install-on-intel-mac.html 提…

Android Studio 历史版本下载

Android Studio 历史版本下载 官方链接&#xff1a;https://developer.android.google.cn/studio/archive 通过gradle插件版本反查Android Studio历史版本 Android Studio Meerkat | 2024.3.1 【https://r1—sn-j5o76n7e.gvt1-cn.com/edgedl/android/studio/install/2024.3.…

有手就行 | Flutter在VSCode(Visual Studio Code)中的安装与配置

目录 一、前言二、资料参考三、版本参考四、Flutter在Visual Studio Code中的安装与配置&#xff08;一&#xff09;下载Flutter插件及SDK&#xff08;二&#xff09;检查开发配置及问题解决&#xff08;1&#xff09;通过代理解决&#xff08;2&#xff09;通过镜像网站解决 五…

任务21:天气信息大屏说明及流程

任务描述 1. 大屏制作流程 1&#xff09;创建DJango项目 2&#xff09;读取MySQL数据&#xff0c;并参照ECharts图形的数据格式进行处理 3&#xff09;参照对照模板、ECharts官网配置项手册及示例&#xff0c;将相应的ECharts图形绘制到大屏对应的容器中。 2. 大屏制作说明…

精英-探索双群协同优化(Elite-Exploration Dual Swarm Cooperative Optimization, EEDSCO)

一种多群体智能优化算法&#xff0c;其核心思想是通过两个分工明确的群体——精英群和探索群——协同工作&#xff0c;平衡算法的全局探索与局部开发能力&#xff0c;从而提高收敛精度并避免早熟收敛。 一 核心概念 在传统优化算法&#xff08;如粒子群优化、遗传算法&#xf…

Go 即时通讯系统:客户端与服务端 WebSocket 通信交互

客户端和服务端的交互 客户端与服务端建立连接 客户端&#xff1a;客户端通过浏览器或者其他应用程序发起一个 HTTP 请求到服务端的 /socket.io 路径。在请求中会携带用户的 UUID 作为参数&#xff08;通过 c.Query("user") 获取&#xff09;。 // router/socket.…

Python 训练营打卡 Day 41

简单CNN 一、数据预处理 在图像数据预处理环节&#xff0c;为提升数据多样性&#xff0c;可采用数据增强&#xff08;数据增广&#xff09;策略。该策略通常不改变单次训练的样本总数&#xff0c;而是通过对现有图像进行多样化变换&#xff0c;使每次训练输入的样本呈现更丰富…

什么是模块化设计?模块和微服务是一样?

软件的模块化设计和微服务是两种不同层次的概念&#xff0c;它们有相似之处但并非等同。以下是详细解释&#xff1a; 一、软件的模块化设计&#xff08;Modular Design&#xff09; 定义 模块化设计是指将一个复杂的软件系统拆分为多个相对独立的模块&#xff08;Module&…

基于千帆大模型的AI体检报告解读系统实战:使用OSS与PDFBox实现PDF内容识别

目录 说明 前言 需求 流程说明 表结构说明 整体流程 百度智能云 注册和实名认证 创建应用 费用说明 大模型API说明 集成大模型 设计Prompt 上传体检报告 读取PDF内容 功能实现 智能评测 抽取大模型工具 功能实现 总结 说明 AI体检报告解读、病例小结或者…