目录

Java-设计模式观察者模式

Java 设计模式:观察者模式

一、模式定义

观察者模式 属于 行为型设计模式 ,用于建立对象间的 一对多依赖关系 。当主题(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);
    }
}
四、两种通知模型对比
特性推模型(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();
    }
}
五、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. GUI事件处理(按钮点击监听)
  3. 实时数据监控(股票价格变动)
  4. 游戏中的成就系统解锁
  5. 分布式配置中心(配置变更通知)
八、高级应用技巧
  1. 异步观察者

    使用线程池处理通知,避免阻塞主题线程

登录后复制

ExecutorService executor = Executors.newCachedThreadPool();

void notifyObservers() {
    for (Observer o : observers) {
        executor.execute(() -> o.update(...));
    }
}
  1. 防止失效监听

    使用弱引用(WeakReference)存储观察者,防止内存泄漏

  2. 保证通知顺序

    使用PriorityQueue实现带优先级的观察者队列

  3. 跨进程观察者

    结合消息队列(如RabbitMQ、Kafka)实现分布式观察者模式

九、相关模式对比
  1. 中介者模式 vs 观察者模式

    中介者集中处理对象间通信,而观察者建立直接订阅关系

  2. 发布-订阅模式 vs 观察者模式

    发布-订阅通过消息代理解耦,观察者是直接通信

十、最佳实践建议
  1. 优先使用拉模型,降低耦合度
  2. 为观察者接口设计合适的更新粒度
  3. 考虑使用CopyOnWriteArrayList保证线程安全
  4. 对于复杂场景,建议使用Guava的EventBus或Spring事件机制