# 什么是观察者模式?一般用在什么场景?

7 min read
Table of Contents

什么是观察者模式?一般用在什么场景?

观察者模式面试标准答案

【核心定义】

观察者模式是一种行为型设计模式,它定义了对象间的一种一对多依赖关系,当一个对象(被观察者/主题)的状态发生改变时,所有依赖于它的对象(观察者)都会自动得到通知并更新。

【关键要点】

  1. 核心角色:包含Subject(被观察者) 和Observer(观察者) 两个核心接口。Subject维护一个观察者列表,并提供注册、删除和通知方法;Observer则定义一个更新接口以响应Subject的状态变化。
  2. 松耦合设计:Subject与Observer之间是抽象耦合,Subject只知道Observer实现了某个接口,而不关心其具体实现,这提高了系统的可扩展性和可维护性。
  3. 推拉模型:
    • 推模型:Subject将变更的详细数据作为参数传递给Observer的更新方法。
    • 拉模型:Subject在通知时只传递自身引用,由Observer主动从Subject中“拉取”所需数据。

【深度推导/细节】

核心矛盾与解决逻辑:

  • 矛盾:多个对象需要依赖另一个对象的状态,但又希望避免紧密耦合,同时状态变更时能自动同步。
  • 解决逻辑:
    1. 抽象分离:定义Observer接口,将依赖方抽象化。
    2. 集中管理:Subject内部维护一个Observer集合,统一管理所有依赖关系。
    3. 事件驱动:Subject状态变更时,遍历Observer集合并调用其更新方法,实现自动通知。
    4. 动态订阅:Observer可随时注册或注销,实现了依赖关系的动态性。

典型实现步骤:

// Step 1: 定义Observer接口
interface Observer {
void update(String message);
}
// Step 2: 实现具体Observer
class 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事件处理)的具体应用,本质思想相同。

【应用场景】

  1. GUI事件处理:按钮点击、鼠标移动等事件监听(Java Swing/AWT,Android onClickListener)。
  2. 消息通知系统:用户订阅消息,当有新消息时自动推送(微博关注、新闻订阅)。
  3. 数据监控与响应:监控系统状态变化,如股票价格变动时自动触发交易策略。
  4. MVC架构:Model作为Subject,View作为Observer,当Model数据变化时自动更新View。
  5. 分布式系统:配置中心配置变更,所有订阅该配置的客户端自动刷新(如ZooKeeper Watcher)。

【实现注意事项】

  1. 线程安全:在多线程环境中,对观察者列表的增删改查需要同步处理,可使用CopyOnWriteArrayList或显式同步。
  2. 通知顺序:观察者的通知顺序通常不应影响业务逻辑,如需顺序保证需明确设计。
  3. 避免循环通知:观察者在update方法中不应反向修改Subject状态,可能导致无限递归。
  4. 内存泄漏:Subject持有Observer引用,需确保Observer及时注销,特别是生命周期较短的对象。

『面试官追问』

  1. 观察者模式有哪些缺点?

    • 隐性成本:观察者不知道其他观察者的存在,可能导致意外更新链。
    • 更新效率:如果观察者数量巨大,遍历通知可能成为性能瓶颈。
    • 调试困难:通知链复杂时,追踪状态变化源头较困难。
  2. Java中有哪些内置的观察者模式实现?

    • java.util.Observable类和java.util.Observer接口(已过时,不推荐使用)。
    • PropertyChangeSupport和PropertyChangeListener(JavaBeans规范)。
    • 各种事件监听器接口(如ActionListener, MouseListener)。
  3. 如何解决观察者模式中的内存泄漏问题?

    • 使用弱引用(WeakReference)持有Observer。
    • 确保在Observer生命周期结束时主动调用detach方法。
    • 在Subject中实现清理机制,定期移除无效的Observer引用。
  4. 观察者模式与回调函数有什么区别?

    • 观察者模式:标准化的多订阅者模式,有明确的接口定义和集合管理。
    • 回调函数:通常是单点的、特定的函数引用,更轻量但缺乏统一管理机制。
    • 回调可视为观察者模式在单观察者场景下的特例简化。
  5. 在分布式系统中如何实现观察者模式?

    • 使用消息队列(如Kafka、RabbitMQ)作为事件通道。
    • 采用WebSocket实现实时推送。
    • 使用服务网格和事件驱动架构(EDA)。

最佳实践建议:在现代Java开发中,更推荐使用响应式编程库(如RxJava、Reactor)或事件总线(如Guava EventBus、Spring Event)来实现观察者模式,它们提供了更强大、类型安全且线程安全的实现。

My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

Comments