Skip to content

观察者模式(Observer Pattern)笔记

定义

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

英文原文定义

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

核心概念

观察者模式建立了主题(Subject)与观察者(Observer)之间的发布-订阅关系,实现了松耦合的状态同步机制。当主题状态变化时,无需知道具体观察者是谁,只需通知已注册的观察者。

主要角色

  1. Subject(主题):维护观察者列表,提供注册/注销接口,状态变化时通知观察者
  2. Observer(观察者):定义更新接口,接收主题通知
  3. ConcreteSubject(具体主题):存储具体状态,状态改变时通知观察者
  4. ConcreteObserver(具体观察者):实现更新接口,保持与主题状态一致

优缺点

优点

  • 松耦合:主题和观察者之间抽象耦合,彼此不知道对方细节
  • 动态关系:可以在运行时动态建立和解除观察关系
  • 广播通信:支持一对多通信,一个主题变化可通知多个观察者
  • 开闭原则:新增观察者无需修改主题代码

缺点

  • 通知顺序不可控:观察者被通知的顺序不确定
  • 性能开销:大量观察者或频繁更新可能导致性能问题
  • 循环依赖:不当使用可能导致循环调用(观察者更新又触发通知)
  • 更新细节不明确:简单的通知机制无法传达状态变化的细节

解决的问题

观察者模式主要解决以下经典问题:

  1. 状态同步:当对象状态变化需要通知其他对象,且不知道具体有多少对象需要通知时
  2. 事件处理:需要实现类似事件处理的机制时
  3. 解耦需求:当需要减少对象间的紧密耦合时
  4. 实时更新:当一个对象的改变需要同时改变其他对象,且不知道具体有多少对象有待改变时

实现注意事项

  1. 推模型 vs 拉模型

    • 推模型:主题将详细数据通过update方法参数发送给观察者
    • 拉模型:观察者收到通知后主动从主题拉取所需数据
  2. 线程安全

    • 在多线程环境中,观察者的注册/注销和通知过程需要同步
    • 考虑使用线程安全的集合类存储观察者列表
  3. 防止内存泄漏

    • 观察者长期不注销可能导致内存泄漏(特别是观察者生命周期短于主题时)
    • 弱引用(WeakReference)可作为解决方案之一
  4. 通知频率控制

    • 实现节流机制避免频繁通知
    • 考虑使用"脏标志"只在必要时通知
  5. 避免观察者方法异常

    • 某个观察者处理异常不应影响其他观察者
    • 考虑异常处理机制

实现变体

  1. 经典实现:严格遵循GoF定义的接口分离设计
  2. 事件总线/消息系统:扩展为更通用的发布-订阅系统
  3. 反应式流:支持背压(back-pressure)的现代变体(如RxJava)
  4. 语言特性集成:某些语言内置观察者模式支持(如C#事件委托)

与其他模式的关系

相似模式区分

  1. 发布-订阅模式

    • 观察者模式:主题直接维护观察者列表,直接通知
    • 发布-订阅:通过消息代理解耦,发布者和订阅者互不知晓
  2. 中介者模式

    • 观察者模式:通过主题协调观察者
    • 中介者模式:通过中介对象封装对象间的交互
  3. 责任链模式

    • 观察者模式:所有观察者都会收到通知
    • 责任链模式:请求沿链传递直到被处理

常见搭配组合

  1. 观察者 + 中介者:用中介者管理复杂的观察关系
  2. 观察者 + 备忘录:观察者记录主题状态变化历史
  3. 观察者 + 单例:主题作为单例供全局访问
  4. 观察者 + 组合:观察者本身可以是组合结构

典型应用场景

  • GUI事件处理(如按钮点击事件)
  • 股票价格变动通知
  • 气象站数据发布
  • 社交媒体订阅/推送
  • 游戏中的成就系统
  • MVC架构中的模型-视图同步

代码示例(推模型实现)

java
// 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update(float temperature, float humidity);
}

// 具体主题
class WeatherData implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private float temperature;
    private float humidity;

    public void setMeasurements(float temperature, float humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(temperature, humidity);
        }
    }
}

// 具体观察者
class CurrentConditionsDisplay implements Observer {
    @Override
    public void update(float temperature, float humidity) {
        System.out.println("Current conditions: " + temperature + "°C and " + humidity + "% humidity");
    }
}