# 什么是观察者模式?一般用在什么场景?
7 min read
Table of Contents
什么是观察者模式?一般用在什么场景?
观察者模式面试标准答案
【核心定义】
观察者模式是一种行为型设计模式,它定义了对象间的一种一对多依赖关系,当一个对象(被观察者/主题)的状态发生改变时,所有依赖于它的对象(观察者)都会自动得到通知并更新。
【关键要点】
- 核心角色:包含Subject(被观察者) 和Observer(观察者) 两个核心接口。Subject维护一个观察者列表,并提供注册、删除和通知方法;Observer则定义一个更新接口以响应Subject的状态变化。
- 松耦合设计:Subject与Observer之间是抽象耦合,Subject只知道Observer实现了某个接口,而不关心其具体实现,这提高了系统的可扩展性和可维护性。
- 推拉模型:
- 推模型:Subject将变更的详细数据作为参数传递给Observer的更新方法。
- 拉模型:Subject在通知时只传递自身引用,由Observer主动从Subject中“拉取”所需数据。
【深度推导/细节】
核心矛盾与解决逻辑:
- 矛盾:多个对象需要依赖另一个对象的状态,但又希望避免紧密耦合,同时状态变更时能自动同步。
- 解决逻辑:
- 抽象分离:定义Observer接口,将依赖方抽象化。
- 集中管理:Subject内部维护一个Observer集合,统一管理所有依赖关系。
- 事件驱动:Subject状态变更时,遍历Observer集合并调用其更新方法,实现自动通知。
- 动态订阅:Observer可随时注册或注销,实现了依赖关系的动态性。
典型实现步骤:
// Step 1: 定义Observer接口interface Observer { void update(String message);}
// Step 2: 实现具体Observerclass ConcreteObserver implements Observer { public void update(String message) { System.out.println("收到更新: " + message); }}
// Step 3: 定义Subject接口及实现class Subject { private List<Observer> observers = new ArrayList<>();
public void attach(Observer o) { observers.add(o); } public void detach(Observer o) { observers.remove(o); }
public void notifyObservers(String message) { for (Observer o : observers) { o.update(message); // 遍历通知所有观察者 } }}【关联/对比】
-
与发布-订阅模式的区别:
- 观察者模式:Subject直接维护Observer引用,直接调用其方法,是直接通信。
- 发布-订阅模式:通过事件通道/消息代理解耦,发布者和订阅者彼此不知对方存在,是间接通信。
- 关键差异:观察者模式是同步的、紧耦合的;发布-订阅模式通常是异步的、松耦合的。
-
与监听器模式的关系:监听器模式是观察者模式在特定场景(如GUI事件处理)的具体应用,本质思想相同。
【应用场景】
- GUI事件处理:按钮点击、鼠标移动等事件监听(Java Swing/AWT,Android onClickListener)。
- 消息通知系统:用户订阅消息,当有新消息时自动推送(微博关注、新闻订阅)。
- 数据监控与响应:监控系统状态变化,如股票价格变动时自动触发交易策略。
- MVC架构:Model作为Subject,View作为Observer,当Model数据变化时自动更新View。
- 分布式系统:配置中心配置变更,所有订阅该配置的客户端自动刷新(如ZooKeeper Watcher)。
【实现注意事项】
- 线程安全:在多线程环境中,对观察者列表的增删改查需要同步处理,可使用
CopyOnWriteArrayList或显式同步。 - 通知顺序:观察者的通知顺序通常不应影响业务逻辑,如需顺序保证需明确设计。
- 避免循环通知:观察者在update方法中不应反向修改Subject状态,可能导致无限递归。
- 内存泄漏:Subject持有Observer引用,需确保Observer及时注销,特别是生命周期较短的对象。
『面试官追问』
-
观察者模式有哪些缺点?
- 隐性成本:观察者不知道其他观察者的存在,可能导致意外更新链。
- 更新效率:如果观察者数量巨大,遍历通知可能成为性能瓶颈。
- 调试困难:通知链复杂时,追踪状态变化源头较困难。
-
Java中有哪些内置的观察者模式实现?
java.util.Observable类和java.util.Observer接口(已过时,不推荐使用)。PropertyChangeSupport和PropertyChangeListener(JavaBeans规范)。- 各种事件监听器接口(如
ActionListener,MouseListener)。
-
如何解决观察者模式中的内存泄漏问题?
- 使用弱引用(WeakReference)持有Observer。
- 确保在Observer生命周期结束时主动调用detach方法。
- 在Subject中实现清理机制,定期移除无效的Observer引用。
-
观察者模式与回调函数有什么区别?
- 观察者模式:标准化的多订阅者模式,有明确的接口定义和集合管理。
- 回调函数:通常是单点的、特定的函数引用,更轻量但缺乏统一管理机制。
- 回调可视为观察者模式在单观察者场景下的特例简化。
-
在分布式系统中如何实现观察者模式?
- 使用消息队列(如Kafka、RabbitMQ)作为事件通道。
- 采用WebSocket实现实时推送。
- 使用服务网格和事件驱动架构(EDA)。
最佳实践建议:在现代Java开发中,更推荐使用响应式编程库(如RxJava、Reactor)或事件总线(如Guava EventBus、Spring Event)来实现观察者模式,它们提供了更强大、类型安全且线程安全的实现。