目录
- [1. 什么是观察者模式](#1. 什么是观察者模式)
- [2. 为什么需要观察者模式](#2. 为什么需要观察者模式)
- [3. 观察者模式的结构](#3. 观察者模式的结构)
- [4. 实现示例](#4. 实现示例)
- [5. Java内置的观察者实现](#5. Java内置的观察者实现)
- [6. 最佳实践与注意事项](#6. 最佳实践与注意事项)
1. 什么是观察者模式
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有观察者都会收到通知并自动更新。
这种模式被广泛应用于事件处理系统中,比如:
- GUI系统中的事件处理
- 消息推送系统
- 订阅发布机制
2. 为什么需要观察者模式
观察者模式主要解决以下问题:
- 解耦合:将观察者和被观察者解耦,使得它们可以独立变化
- 实现一对多关系:一个对象的状态改变可以通知多个订阅者
- 支持广播通信:不需要明确指定接收者,所有订阅者都能收到通知
3. 观察者模式的结构
UML类图
Subject +attach(Observer observer) +detach(Observer observer) +notify() ConcreteSubject -state +getState() +setState() Observer +update() ConcreteObserver -state +update()
核心角色
- Subject(主题) :
- 持有观察者集合
- 提供注册和删除观察者的方法
- 通知所有观察者的方法
- Observer(观察者) :
- 定义更新接口
- 收到主题通知时进行更新
- ConcreteSubject(具体主题) :
- Subject 的具体实现类
- 存储观察者感兴趣的状态
- 当状态发生改变时通知观察者
- ConcreteObserver(具体观察者) :
- Observer 的具体实现类
- 维护一个指向 ConcreteSubject 对象的引用
- 存储感兴趣的状态,这些状态需要和主题的状态保持一致
- 实现 Observer 的更新接口,使自身状态与主题的状态保持一致
4. 实现示例
让我们通过一个具体的例子来理解观察者模式。假设我们要实现一个天气监测系统:
java
// 观察者接口
public interface WeatherObserver {
void update(float temperature, float humidity, float pressure);
}
// 主题接口
public interface WeatherSubject {
void registerObserver(WeatherObserver observer);
void removeObserver(WeatherObserver observer);
void notifyObservers();
}
// 具体主题:气象站
public class WeatherStation implements WeatherSubject {
private List<WeatherObserver> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherStation() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(WeatherObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(WeatherObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (WeatherObserver observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
// 当气象测量值改变时调用此方法
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers();
}
}
// 具体观察者:显示当前天气
public class CurrentConditionsDisplay implements WeatherObserver {
private float temperature;
private float humidity;
private float pressure;
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("当前温度: " + temperature + "°C");
System.out.println("当前湿度: " + humidity + "%");
System.out.println("当前气压: " + pressure + "hPa");
}
}
// 使用示例
public class WeatherStationDemo {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
CurrentConditionsDisplay display = new CurrentConditionsDisplay();
// 注册观察者
weatherStation.registerObserver(display);
// 更新天气数据
weatherStation.setMeasurements(25.2f, 65.0f, 1013.1f);
}
}
运行结果:
plain
当前温度: 25.2°C
当前湿度: 65.0%
当前气压: 1013.1hPa
运行结果说明:
- 首先创建了一个
WeatherStation
对象(主题)和一个CurrentConditionsDisplay
对象(观察者) - 通过
registerObserver()
方法将显示器注册为气象站的观察者 - 当调用
setMeasurements()
更新气象数据时:- 气象站内部的数据被更新
- 自动调用
notifyObservers()
通知所有观察者 - 观察者的
update()
方法被调用,接收最新数据 - 显示器随即更新显示最新的气象信息
这个简单的例子展示了观察者模式的核心工作流程:对象状态改变 → 自动通知 → 观察者更新,整个过程中主题和观察者都保持松耦合。
5. Java内置的观察者实现
Java提供了内置的观察者模式支持,通过java.util.Observable
类和java.util.Observer
接口:
java
import java.util.Observable;
import java.util.Observer;
// 继承Observable的气象站
public class JavaWeatherStation extends Observable {
private float temperature;
private float humidity;
private float pressure;
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
setChanged(); // 标记状态已改变
notifyObservers(new float[]{temperature, humidity, pressure});
}
}
// 实现Observer接口的显示器
public class JavaCurrentConditionsDisplay implements Observer {
@Override
public void update(Observable observable, Object arg) {
if (arg instanceof float[]) {
float[] measurements = (float[]) arg;
float temperature = measurements[0];
float humidity = measurements[1];
float pressure = measurements[2];
System.out.println("当前温度: " + temperature + "°C");
System.out.println("当前湿度: " + humidity + "%");
System.out.println("当前气压: " + pressure + "hPa");
}
}
}
注意:从Java 9开始,Observable
和Observer
已被标记为废弃,建议使用其他替代方案,如Flow API或自定义实现。
6. 最佳实践与注意事项
- 避免循环依赖 :
- 确保观察者和主题之间不会形成循环依赖
- 注意在通知观察者时不要触发新的更新循环
- 内存管理 :
- 及时移除不再需要的观察者
- 注意防止内存泄漏
- 线程安全 :
- 在多线程环境下需要确保观察者列表的线程安全
- 考虑使用线程安全的集合类
- 通知顺序 :
- 不要依赖观察者的通知顺序
- 观察者的处理逻辑应该相互独立
- 异常处理 :
- 观察者的更新方法应该处理异常
- 一个观察者的异常不应影响其他观察者
使用场景
观察者模式适用于以下场景:
- 当一个对象的改变需要同时改变其他对象时
- 当一个对象必须通知其他对象,而它又不知道这些对象是谁时
- 当一个对象改变时,需要动态更新一组对象时
优点
- 符合开闭原则
- 可以在运行时建立对象之间的关系
- 支持广播通信
缺点
- 观察者模式可能导致广播风暴
- 如果观察者太多,通知的代价会很大
- 观察者之间的依赖关系不易跟踪
总结
观察者模式是一种强大的设计模式,它通过松耦合的方式实现了对象之间的一对多的依赖关系。通过合理使用观察者模式,我们可以构建出灵活、可维护的事件处理系统。在实际应用中,要根据具体场景选择合适的实现方式,并注意处理好各种边界情况。