Java 设计模式:观察者模式

一、模式定义

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

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

登录后复制

plain 复制代码
// 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) |
| 数据传递方式 | 主题主动发送完整数据 | 观察者从主题拉取所需数据 |
| 实现复杂度 | 观察者可能收到不需要的数据 | 观察者按需获取数据,需要维护主题引用 |
| 耦合度 | 观察者依赖具体数据结构 | 观察者只需知道主题存在 |
| 性能考量 | 可能传输冗余数据 | 需要多次调用获取方法 |

拉模型改进示例:
登录后复制

plain 复制代码
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内置支持

登录后复制

plain 复制代码
// 已过时,不推荐使用,仅作了解
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. 异步观察者
    使用线程池处理通知,避免阻塞主题线程

登录后复制

plain 复制代码
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事件机制
相关推荐
九溪弥烟、1 分钟前
python编写的一个打砖块小游戏
开发语言·python·pygame
java技术小馆16 分钟前
责任链模式如何减少模块之间的耦合
java·数据库·设计模式·责任链模式
stars18 分钟前
搞定python之三----序列、字典及集合
开发语言·python
Matrix7018 分钟前
Scala编程_实现Rational的基本操作
开发语言·python·scala
0wioiw026 分钟前
C++基础(VScode环境安装)
开发语言·c++
hhw19911227 分钟前
c#面试题整理7
开发语言·c#
大溪地C31 分钟前
Spring Boot3整合Knife4j(4.5.0)
java·数据库·spring boot
Java&Develop31 分钟前
java项目springboot 项目启动不了解决方案
java·开发语言·spring boot
qq_2573795936 分钟前
python基础-字符串速查笔记
开发语言·笔记·python
一条闲鱼_mytube41 分钟前
golang recover错误
开发语言·后端·golang