欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > 观察者模式:从博客订阅到消息队列的解耦实践

观察者模式:从博客订阅到消息队列的解耦实践

2025/4/21 18:01:18 来源:https://blog.csdn.net/qq_17589751/article/details/147309700  浏览:    关键词:观察者模式:从博客订阅到消息队列的解耦实践

观察者模式:从博客订阅到消息队列的解耦实践

一、模式核心:用事件驱动实现对象间松耦合

在新闻 APP 中,当热点事件发生时需要实时通知所有订阅用户;在电商系统中,库存变化需触发价格监控模块重新计算。这类场景的核心矛盾是:对象间存在依赖关系,但不能硬编码耦合。** 观察者模式(Observer Pattern)** 通过定义「发布 - 订阅」模型,让对象间的通知关系完全解耦,核心解决:

  • 数据变更通知:当主题(Subject)状态变化时,自动通知所有关联的观察者(Observer)
  • 模块解耦:观察者与主题无需互相知道具体实现,仅通过抽象接口交互

核心思想与 UML 类图

img

二、核心实现:构建通用事件通知框架

1. 定义主题接口(Subject)

import java.util.ArrayList;
import java.util.List;public abstract class Subject {private List<Observer> observers = new ArrayList<>();// 注册观察者public void attach(Observer observer) {observers.add(observer);}// 注销观察者public void detach(Observer observer) {observers.remove(observer);}// 通知所有观察者protected void notifyObservers(Object data) {observers.forEach(observer -> observer.update(data));}// 抽象方法:主题状态变化时调用public abstract void updateState(Object newState);
}

2. 实现具体主题(ConcreteSubject)

public class StockSubject extends Subject {private double stockPrice; // 主题状态:股票价格public double getStockPrice() {return stockPrice;}@Overridepublic void updateState(Object newState) {this.stockPrice = (double) newState;notifyObservers(stockPrice); // 状态变化时触发通知}
}

3. 定义观察者接口(Observer)

public interface Observer {// 接收主题通知的更新方法void update(Object data);
}

4. 实现具体观察者(ConcreteObserver)

public class InvestorObserver implements Observer {private String investorName;public InvestorObserver(String name) {this.investorName = name;}@Overridepublic void update(Object data) {double price = (double) data;System.out.println("投资者 " + investorName + " 收到通知:股票价格更新为 " + price);// 执行具体业务逻辑(如触发交易策略)}
}

5. 客户端调用示例

public class ClientDemo {public static void main(String[] args) {// 创建主题与观察者StockSubject stock = new StockSubject();Observer investorA = new InvestorObserver("张三");Observer investorB = new InvestorObserver("李四");// 注册观察者stock.attach(investorA);stock.attach(investorB);// 主题状态变化,触发通知stock.updateState(15.5); // 输出:两位投资者收到价格更新通知stock.detach(investorB); // 注销观察者Bstock.updateState(16.2); // 仅投资者A收到通知}
}

三、进阶:实现异步事件驱动与精准通知

1. 支持泛型的强类型通知

// 定义带泛型的观察者接口
public interface GenericObserver<T> {void update(T data);
}// 主题支持特定类型的事件数据
public class GenericSubject<T> {private List<GenericObserver<T>> observers = new ArrayList<>();public void notifyObservers(T data) {observers.forEach(observer -> observer.update(data));}
}// 使用示例:股票价格变化事件(Double类型)
GenericSubject<Double> stock = new GenericSubject<>();
stock.attach((Double price) -> System.out.println("精准通知:价格" + price));

2. 异步通知机制(解耦通知与业务处理)

public class AsyncSubject<T> extends GenericSubject<T> {private ExecutorService executor = Executors.newFixedThreadPool(5);@Overridepublic void notifyObservers(T data) {// 使用线程池异步执行观察者逻辑executor.submit(() -> super.notifyObservers(data));}
}// 优势:避免主题阻塞,提升系统吞吐量
// 注意:需处理异步带来的线程安全与数据一致性问题

3. 带事件类型的精准通知(基于 Guava EventBus)

// 定义具体事件类型
public class PriceUpdateEvent {private double newPrice;public PriceUpdateEvent(double price) {this.newPrice = price;}// getters...
}// 使用Guava EventBus实现
EventBus eventBus = new EventBus();
eventBus.register(new Object() {@Subscribepublic void onPriceUpdate(PriceUpdateEvent event) {// 仅接收PriceUpdateEvent类型的事件System.out.println("处理价格更新事件:" + event.getNewPrice());}
});
eventBus.post(new PriceUpdateEvent(18.7)); // 精准触发对应观察者

四、框架与源码中的观察者模式实践

1. Spring ApplicationEvent(同步通知)

  • 核心类ApplicationEvent(事件)、ApplicationListener(观察者)、ApplicationEventPublisher(主题)

  • 使用示例:

    // 定义自定义事件
    public class OrderPaidEvent extends ApplicationEvent {private String orderId;public OrderPaidEvent(Object source, String orderId) {super(source);this.orderId = orderId;}
    }// 注册观察者
    @Component
    public class OrderListener implements ApplicationListener<OrderPaidEvent> {@Overridepublic void onApplicationEvent(OrderPaidEvent event) {// 处理订单支付后的业务(如更新库存、发送短信)}
    }// 发布事件
    applicationEventPublisher.publishEvent(new OrderPaidEvent(this, "1001"));
    

2. Android Listener 机制(异步回调)

  • 典型场景:按钮点击事件、网络请求回调

  • 实现原理:

    // 主题(Button)注册观察者(OnClickListener)
    button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 观察者逻辑}
    });
    

3. Kafka 消息队列(分布式观察者)

  • 主题:Kafka 的 Topic(如 “stock-price-topic”)
  • 观察者:Kafka 消费者组(Consumer Group)
  • 优势:支持百万级观察者的分布式通知,实现系统间的松耦合
// Kafka消费者示例(观察者)
KafkaConsumer<String, Double> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("stock-price-topic"));
while (true) {ConsumerRecords<String, Double> records = consumer.poll(Duration.ofMillis(100));for (ConsumerRecord<String, Double> record : records) {handlePriceUpdate(record.value()); // 处理通知}
}

五、避坑指南:正确使用观察者模式的 4 个要点

1. 避免内存泄漏(及时注销观察者)

  • ❌ 错误实践:观察者注册后未调用detach(),导致主题持有观察者强引用

  • ✅ 最佳实践:在观察者销毁时(如 Activity 销毁),主动从主题中注销

    @Override
    protected void onDestroy() {super.onDestroy();subject.detach(this); // 注销当前观察者
    }
    

2. 控制通知粒度(避免过度频繁通知)

  • 当主题状态变化频繁时(如实时数据流),增加防抖机制批量通知

    // 防抖示例:1秒内多次变化合并为一次通知
    public void updateState(Object newState) {if (lastUpdateTime + 1000 > System.currentTimeMillis()) {return; // 忽略短时间内的重复通知}lastUpdateTime = System.currentTimeMillis();super.updateState(newState);
    }
    

3. 处理循环依赖(观察者反向修改主题)

  • 当观察者更新时可能再次触发主题状态变化,需增加

    事件防火墙

    public void update(Object data) {if (isProcessingEvent) return; // 避免循环触发isProcessingEvent = true;// 业务逻辑isProcessingEvent = false;
    }
    

4. 反模式:滥用观察者导致复杂度上升

  • 当观察者链过深(如 A→B→C→D)时,改用责任链模式事件总线重构
  • 避免为简单的双向通信使用观察者(直接调用接口更高效)

六、总结:何时该用观察者模式?

适用场景核心特征典型案例
实时数据通知一个对象状态变化需触发多个对象的响应股票行情推送、邮件订阅系统
模块解耦需求对象间存在依赖但不想硬编码关联微服务事件驱动架构、GUI 事件处理
异步事件处理需要将通知与业务逻辑分离消息队列、日志异步写入

观察者模式通过「主题抽象 + 事件驱动」的设计,将对象间的依赖关系从「直接调用」转化为「事件订阅」,这是实现松耦合系统的核心模式之一。下一篇我们将深入探讨状态模式,解析有限状态机在电商订单系统中的设计与实现,敬请期待!

扩展思考:观察者模式 vs 发布 - 订阅模式

两者核心思想相似,但存在关键区别:

模式中介者通知方式应用场景
观察者模式主题直接关联观察者主题主动通知观察者本地模块间通信
发布 - 订阅模式事件总线作为中介发布者与订阅者无直接关联分布式系统、微服务间通信

理解这些差异,有助于在不同架构场景中选择最合适的设计方案。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词