一、模式定义

观察者模式属于行为型设计模式,用于建立对象间的一对多依赖关系。当主题(Subject)状态变化时,所有依赖的观察者(Observer)会自动收到通知并更新。

二、核心角色
  1. Subject(主题)
  • 维护观察者列表,提供添加/删除观察者的方法
  • 定义通知观察者的方法
  1. Observer(观察者接口)
  • 定义更新接口,用于接收主题通知
  1. ConcreteSubject(具体主题)
  • 存储具体状态信息
  • 状态改变时触发通知
  1. ConcreteObserver(具体观察者)
  • 实现更新逻辑,保持与主题状态同步
三、经典实现(以气象站为例)
// 1. 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 2. 观察者接口
interface Observer {
    void update(float temp, float humidity, float pressure);
}

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

    @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, pressure);
        }
    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

// 4. 具体观察者实现
class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Subject weatherData) {
        weatherData.registerObserver(this);
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        display();
    }

    public void display() {
        System.out.println("Current conditions: " + temperature 
            + "°C and " + humidity + "% humidity");
    }
}

// 5. 使用示例
public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay display = new CurrentConditionsDisplay(weatherData);
        
        weatherData.setMeasurements(25.5f, 65, 1013.1f);
        weatherData.setMeasurements(26.8f, 70, 1012.5f);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
四、两种通知模型对比

特性

推模型(Push)

拉模型(Pull)

数据传递方式

主题主动发送完整数据

观察者从主题拉取所需数据

实现复杂度

观察者可能收到不需要的数据

观察者按需获取数据,需要维护主题引用

耦合度

观察者依赖具体数据结构

观察者只需知道主题存在

性能考量

可能传输冗余数据

需要多次调用获取方法

拉模型改进示例:

interface Subject {
    // 新增获取状态的方法
    float getTemperature();
    float getHumidity();
    float getPressure();
}

class WeatherData implements Subject {
    // 保持原有接口不变
    // 新增状态获取方法...
}

class CurrentConditionsDisplay implements Observer {
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void update() {
        this.temperature = weatherData.getTemperature();
        this.humidity = weatherData.getHumidity();
        display();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
五、Java内置支持
// 已过时,不推荐使用,仅作了解
import java.util.Observable;
import java.util.Observer;

class WeatherData extends Observable {
    public void measurementsChanged() {
        setChanged();  // 必须调用
        notifyObservers();  // 无参为拉模型
        // notifyObservers(data);  // 带参为推模型
    }
}

class Display implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            // 拉取数据
            WeatherData wd = (WeatherData) o;
            // 或使用arg参数获取推送数据
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
六、模式优劣分析

优势:

  • 符合开闭原则:新增观察者无需修改主题
  • 运行时动态建立对象关系
  • 实现抽象耦合,主题无需知道具体观察者

劣势:

  • 通知顺序不可控
  • 频繁更新可能影响性能
  • 循环依赖风险
七、应用场景
  1. 跨系统事件通知(如订单状态更新)
  2. GUI事件处理(按钮点击监听)
  3. 实时数据监控(股票价格变动)
  4. 游戏中的成就系统解锁
  5. 分布式配置中心(配置变更通知)
八、高级应用技巧
  1. 异步观察者
    使用线程池处理通知,避免阻塞主题线程
ExecutorService executor = Executors.newCachedThreadPool();

void notifyObservers() {
    for (Observer o : observers) {
        executor.execute(() -> o.update(...));
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  1. 防止失效监听
    使用弱引用(WeakReference)存储观察者,防止内存泄漏
  2. 保证通知顺序
    使用PriorityQueue实现带优先级的观察者队列
  3. 跨进程观察者
    结合消息队列(如RabbitMQ、Kafka)实现分布式观察者模式
九、相关模式对比
  1. 中介者模式 vs 观察者模式
    中介者集中处理对象间通信,而观察者建立直接订阅关系
  2. 发布-订阅模式 vs 观察者模式
    发布-订阅通过消息代理解耦,观察者是直接通信
十、最佳实践建议
  1. 优先使用拉模型,降低耦合度
  2. 为观察者接口设计合适的更新粒度
  3. 考虑使用CopyOnWriteArrayList保证线程安全
  4. 对于复杂场景,建议使用Guava的EventBus或Spring事件机制