news 2026/6/21 6:46:18

观察者模式:从理论到生产实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
观察者模式:从理论到生产实践

观察者模式深度解析:从理论到生产实践,Spring都在用的设计模式

观察者模式UML类图

在软件开发中,我们经常需要实现”一个对象状态变化,多个对象自动更新”的场景。比如用户注册成功时,需要发送欢迎邮件、赠送积分、记录日志等多个操作。这种一对多的依赖关系,正是观察者模式的典型应用场景。

观察者模式是什么?

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

这里借用建筑监理的比喻:投资方(观察者们)委托建筑师(主题)监督项目进度。每当有建筑进展,建筑师主动通知所有投资方,无需投资方反复询问。

核心角色与实现

观察者模式主要包含四个角色:

  1. Subject(主题):定义了被观察者对象的基本接口
  2. ConcreteSubject(具体主题):主题的具体实现,维护观察者列表
  3. Observer(观察者):定义了观察者对通知做出反应的方法
  4. ConcreteObserver(具体观察者):实现Observer接口,观察主题并执行相应操作

下面通过一个实际的电商库存管理示例来展示观察者模式的实现:

// 观察者接口 public interface InventoryObserver { void onInventoryChanged(Product product, int newQuantity); } // 具体观察者1:库存预警系统 public class InventoryAlert implements InventoryObserver { @Override public void onInventoryChanged(Product product, int newQuantity) { if (newQuantity < 10) { System.out.println("⚠️ 库存预警: " + product.getName() + " 库存不足,当前数量: " + newQuantity); } } } // 具体观察者2:订单处理系统 public class OrderProcessor implements InventoryObserver { @Override public void onInventoryChanged(Product product, int newQuantity) { if (newQuantity < 5) { System.out.println("📦 自动触发补货: " + product.getName() + " 正在向供应商下单..."); } } } // 具体观察者3:价格调整系统 public class PriceAdjuster implements InventoryObserver { @Override public void onInventoryChanged(Product product, int newQuantity) { if (newQuantity > 100) { System.out.println("💰 库存充足,可以考虑促销降价!"); } } } // 主题接口 public interface InventorySubject { void addObserver(InventoryObserver observer); void removeObserver(InventoryObserver observer); void notifyObservers(Product product, int quantity); } // 具体主题:库存管理器 public class InventoryManager implements InventorySubject { private final List<InventoryObserver> observers = new ArrayList<>(); private final Map<String, Integer> inventory = new HashMap<>(); @Override public void addObserver(InventoryObserver observer) { observers.add(observer); System.out.println("添加观察者: " + observer.getClass().getSimpleName()); } @Override public void removeObserver(InventoryObserver observer) { observers.remove(observer); System.out.println("移除观察者: " + observer.getClass().getSimpleName()); } @Override public void notifyObservers(Product product, int quantity) { for (InventoryObserver observer : observers) { observer.onInventoryChanged(product, quantity); } } public void updateStock(String productId, int quantity) { inventory.put(productId, quantity); Product product = new Product(productId, "Product-" + productId); notifyObservers(product, quantity); } } // 使用示例 public class ObserverDemo { public static void main(String[] args) { // 创建库存管理器(主题) InventoryManager manager = new InventoryManager(); // 创建观察者 InventoryAlert alert = new InventoryAlert(); OrderProcessor orderProcessor = new OrderProcessor(); PriceAdjuster priceAdjuster = new PriceAdjuster(); // 注册观察者 manager.addObserver(alert); manager.addObserver(orderProcessor); manager.addObserver(priceAdjuster); System.out.println("\n--- 模拟库存变化 ---"); manager.updateStock("iphone", 3); // 库存不足场景 System.out.println("\n--- 模拟供应商补货 ---"); manager.updateStock("iphone", 50); // 补货后库存正常 System.out.println("\n--- 模拟库存积压 ---"); manager.updateStock("iphone", 150); // 库存积压场景 } }

运行结果:

添加观察者: InventoryAlert 添加观察者: OrderProcessor 添加观察者: PriceAdjuster --- 模拟库存变化 --- ⚠️ 库存预警: Product-iphone 库存不足,当前数量: 3 📦 自动触发补货: Product-iphone 正在向供应商下单... --- 模拟供应商补货 --- --- 模拟库存积压 --- 💰 库存充足,可以考虑促销降价!

Spring框架中的事件机制

Spring框架中的事件机制

观察者模式在Spring框架中被广泛应用,其事件机制就是典型的实现。Spring的事件机制以ApplicationContext为核心,实现了观察者模式的高级封装:

// 自定义事件 public class UserRegisterEvent extends ApplicationEvent { private final String username; public UserRegisterEvent(Object source, String username) { super(source); this.username = username; } public String getUsername() { return username; } } // 事件监听器(观察者) @Component @EventListener public class EmailSendListener { @EventListener(classes = UserRegisterEvent.class) public void sendEmail(UserRegisterEvent event) { System.out.println("📧 发送欢迎邮件给: " + event.getUsername()); } } @Component @EventListener public class PointsGrantListener { @EventListener(classes = UserRegisterEvent.class) public void grantPoints(UserRegisterEvent event) { System.out.println("🎁 为新用户 " + event.getUsername() + " 赠送100积分"); } } @Component @EventListener public class LogRecordListener { @EventListener(classes = UserRegisterEvent.class) public void recordLog(UserRegisterEvent event) { System.out.println("📝 记录用户注册日志: " + event.getUsername()); } } // 事件发布者 @RestController @RequestMapping("/api/users") public class UserController { @Autowired private ApplicationEventPublisher eventPublisher; @PostMapping("/register") public ResponseEntity<String> register(@RequestParam String username) { // 注册用户... System.out.println("创建用户: " + username); // 发布事件 UserRegisterEvent event = new UserRegisterEvent(this, username); eventPublisher.publishEvent(event); return ResponseEntity.ok("注册成功"); } }

Spring的事件机制优势:

  • 完全解耦:发布者和订阅者互不感知
  • 异步支持:支持@Async异步事件处理
  • 事务绑定:@TransactionalEventListener支持事务提交后触发
  • 条件过滤:@EventListener支持SpEL条件表达式

消息队列中的观察者模式

消息队列系统(如RabbitMQ、Apache Kafka)本质上也是观察者模式的延伸,但采用了更灵活的发布-订阅模式:

// 配置多队列消息监听 @Component @RabbitListener public class OrderEventHandler { // 监听订单创建事件 @RabbitListener(queues = "orderCreated") public void handleOrderCreated(Order order) { log.info("订单创建事件: {}", order.getId()); // 1. 减库存 inventoryService.reduceStock(order); // 2. 发送确认邮件 emailService.sendOrderConfirm(order); // 3. 记录业务日志 auditService.log(order); } // 监听订单支付事件 @RabbitListener(queues = "orderPaid") public void handleOrderPaid(String orderId) { log.info("订单支付事件: {}", orderId); // 1. 更新订单状态 orderService.payOrder(orderId); // 2. 通知物流系统发货 logisticsService.shipOrder(orderId); // 3. 更新用户累计消费 userService.addConsumption(orderId); } // 监听订单取消事件 @RabbitListener(queues = "orderCancelled") public void handleOrderCancelled(Order order) { log.info("订单取消事件: {}", order.getId()); // 1. 恢复库存 inventoryService.restoreStock(order); // 2. 处理退款 paymentService.refund(order); // 3. 发送取消通知 notificationService.orderCancelled(order); } }

观察者模式 vs 发布-订阅模式

观察者模式 vs 发布-订阅模式

很多人将观察者模式和发布-订阅模式混为一谈,实际上两者有明显区别:

特性观察者模式发布-订阅模式
耦合程度相对较强(主题需维护观察者列表)完全解耦(通过消息中介)
通信方式同步通信支持异步通信
分层结构两层(主题-观察者)三层(发布者-临时层-订阅者)
消息过滤不支持支持(如topic)
适用场景同一应用中分布式系统

观察者模式工作流程

观察者模式工作流程

观察者模式的工作流程主要分为以下几个步骤:

  1. 注册阶段:观察者调用subscrib方法将自己注册到主题
  2. 状态变更:主题对象的内部状态发生改变
  3. 通知阶段:主题调用notify方法通知所有注册的观察者
  4. 更新阶段:观察者收到通知后,调用对象的update方法更新自身状态

应用场景分析

应用场景

观察者模式广泛应用于以下场景:

1. GUI组件事件监听

// JavaFX 按钮点击监听 Button button = new Button("点击我"); button.setOnAction(event -> { System.out.println("按钮被点击!"); });

2. 实时数据同步

// 数据库binlog监听(基于Canal) @Component public class DatabaseChangeHandler extends CanalClientEx { @EventListener public void handleChange(CanalEntry.EventType change) { // 同步到ES elasticsearchService.sync(change); // 同步到缓存 cacheService.sync(change); // 通知下游服务 messageService.publish(change); } }

实现要点与注意事项

1. 观察者管理

// 使用线程安全的集合管理观察者 private final List<Observer> observers = Collections.synchronizedList(new ArrayList<>()); // 添加观察者 public void addObserver(Observer observer) { if (observer == null) { throw new IllegalArgumentException("观察者不能为空"); } synchronized(observers) { observers.add(observer); } } // 移除观察者 public void removeObserver(Observer observer) { observers.remove(observer); }

2. 防止观察者循环引用

public class ConcreteSubject extends Observable { private boolean changed = false; @Override void notifyObservers() { // 使用锁避免循环引用 synchronized (this) { if (!changed) { return; } // 复制观察者列表避免并发修改 Observer[] arr = observers.toArray(new Observer[0]); clearChanged(); for (Observer observer : arr) { try { observer.update(this, null); } catch (Exception e) { log.warn("通知观察者失败:", e); } } } } }

3. 异步通知避免阻塞

// 使用线程池异步通知 @Component public class AsyncEventPublisher { private final ExecutorService executor = Executors.newCachedThreadPool(); public void publishEventAsync(InventoryEvent event) { executor.execute(() -> { try { notifyObservers(event); } catch (Exception e) { log.error("异步事件通知失败", e); } }); } // 优雅关闭 @PreDestroy public void shutdown() { executor.shutdown(); } }

4. 优先级排序

// 支持优先级的事件监听器 public class PriorityEventRegistry { private final PriorityQueue<EventListener> listeners = new PriorityQueue<>(Comparator.comparing(EventListener::getPriority)); public void registerListener(EventListener listener) { listeners.add(listener); } }

优缺点分析

优点:

  1. 降低耦合度:发布者和订阅者相互解耦,易于系统扩展
  2. 遵守开闭原则:新增观察者时无需修改主题代码
  3. 实现广播通信:一个主题状态变化,多个观察者同步更新
  4. 消息传递高效:直接方法调用,性能优于消息队列

缺点:

  1. 观察者数量与性能相关:当观察者数量较多时,通知过程变慢
  2. 循环依赖风险:观察者和主题之间可能产生循环引用
  3. 同步通知阻塞所有观察者:一个观察者阻塞会影响整个通知链

观察者模式作为最经典的设计模式之一,它的思想贯穿于各种事件处理和通知机制中。从简单的对象监听,到复杂的分布式事件系统,观察者模式都能发挥其解耦和扩展性的优势。掌握观察者模式,能够帮助我们构建更加灵活、可维护的软件架构。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 6:52:42

2025最新!专科生必备9个AI论文平台测评与推荐

2025最新&#xff01;专科生必备9个AI论文平台测评与推荐 2025年专科生论文写作工具测评&#xff1a;为何需要一份精准指南&#xff1f; 随着人工智能技术的快速发展&#xff0c;AI论文平台逐渐成为高校学生&#xff0c;尤其是专科生群体的重要辅助工具。然而&#xff0c;面对市…

作者头像 李华
网站建设 2026/6/19 23:47:52

Open-AutoGLM部署实战(千卡级优化秘籍)

第一章&#xff1a;Open-AutoGLM部署实战&#xff08;千卡级优化秘籍&#xff09;在超大规模模型训练场景中&#xff0c;Open-AutoGLM 的千卡级集群部署对性能调优提出了极高要求。合理的资源配置与通信优化策略是实现线性加速比的关键。分布式训练架构设计 采用混合并行策略&a…

作者头像 李华
网站建设 2026/6/9 21:59:15

【AI自动化工具下载指南】:智普Open-AutoGLM获取路径全解析

第一章&#xff1a;智普Open-AutoGLM如何下载 访问官方仓库 智普AI推出的Open-AutoGLM是一个开源的自动化代码生成工具&#xff0c;其源码托管在GitHub平台。用户需首先访问项目主页以获取最新版本的下载链接。 打开浏览器&#xff0c;访问 https://github.com/zhipuai/Open-…

作者头像 李华
网站建设 2026/6/14 13:14:58

模型部署效率提升300%?Open-AutoGLM轻量化配置秘籍曝光

第一章&#xff1a;模型部署效率提升300%&#xff1f;Open-AutoGLM轻量化之谜在大模型时代&#xff0c;推理延迟与资源消耗成为制约AI落地的关键瓶颈。Open-AutoGLM作为开源社区新兴的轻量化自动推理框架&#xff0c;凭借其独特的模型压缩策略与运行时优化机制&#xff0c;宣称…

作者头像 李华
网站建设 2026/6/20 8:41:50

RAG架构演进全解析:从基础到智能化的四代架构之路

本文系统梳理了RAG架构从基础到智能化的四代演进历程&#xff0c;详细分析了Naive、Advanced、Modular和Agentic RAG的核心特点与技术突破。文章展示了RAG如何通过模块化设计、智能体协同解决知识更新、语义对齐和复杂任务处理等问题&#xff0c;为LLM应用落地提供架构参考&…

作者头像 李华
网站建设 2026/6/12 6:34:08

如何使用JMeter测试https请求

HTTP与HTTPS略有不同&#xff0c;所以第一次使用JMeter测试https请求时遇到了问题&#xff0c;百度一番后找到解决方法&#xff1a;加载证书。 下面内容主要记录这次操作&#xff0c;便于后续参考&#xff1a; 操作浏览器&#xff1a;谷歌 &#xff08;1&#xff09;下载被测…

作者头像 李华