news 2026/5/30 13:54:36

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

作者头像

张小明

前端开发工程师

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/5/30 13:53:50

格栅除污机数据采集远程运维系统方案

格栅除污机作为污水处理、水利工程、市政排水等领域的关键预处理设备&#xff0c;其清污效率、运行稳定性及运维及时性直接影响后续处理工艺的顺畅性、水体净化效果与系统运行能耗&#xff0c;是保障水处理设施长效运转、防范管网堵塞风险的核心基础设施。对设备制造商来说&…

作者头像 李华
网站建设 2026/5/30 3:24:35

Microsoft Dataverse 远程代码执行高危漏洞 (CVE-2024-35260) 安全公告

安全公告&#xff1a;Microsoft Dataverse 远程代码执行漏洞 (CVE-2024-35260) 概述 在 Microsoft Dataverse 中发现了一个严重的远程代码执行漏洞&#xff0c;被分配为 CVE-2024-35260。该漏洞被归类为 CWE-426&#xff08;不可信搜索路径&#xff09;&#xff0c;允许具有高权…

作者头像 李华
网站建设 2026/5/20 23:26:43

智普Open-AutoGLM如何安全下载?资深工程师亲授3大避坑要点

第一章&#xff1a;智普Open-AutoGLM下载前的准备工作 在开始下载和使用智普AI推出的Open-AutoGLM模型之前&#xff0c;需完成一系列环境与权限配置&#xff0c;以确保后续流程顺利进行。合理的前期准备不仅能提升部署效率&#xff0c;还能避免常见依赖冲突与认证失败问题。 检…

作者头像 李华
网站建设 2026/5/22 21:25:44

SPL量化工作台使用教程-12 信号输出与实操

在实操时如何用策略来指导下单&#xff1f;这里所讲的策略都是离线策略&#xff0c;适用于低频交易。在离线策略中&#xff0c;可由系统计算出下单信号&#xff0c;然后人工操作下单买卖。这样我们只需在系统中根据策略计算出当天是否有下单信号就可以了&#xff0c;此时可以使…

作者头像 李华