一、设计模式概述
1.1 核心思想
观察者模式(Observer Pattern)定义了对象间的一种一对多依赖关系,当一个对象(被观察者/主题)的状态发生改变时,所有依赖于它的对象(观察者)都会自动收到通知并更新。
1.2 UML类图结构
scss
复制
┌─────────────┐ ┌─────────────┐
│ Subject │<---->│ Observer │
├─────────────┤ ├─────────────┤
│+attach(obs) │ │+update() │
│+detach(obs) │ └─────────────┘
│+notify() │ △
└─────────────┘ |
△ |
| ┌─────┴─────┐
| │ │
┌─────────────┐ ┌─────────┐ ┌─────────┐
│ConcreteSubject│ │Concrete │ │Concrete │
│ │ │Observer1│ │Observer2│
├─────────────┤ ├─────────┤ ├─────────┤
│+getState() │ │+update()│ │+update()│
│+setState() │ └─────────┘ └─────────┘
└─────────────┘
二、完整Java代码实现
2.1 基本实现
csharp
java
java
下载
复制
// 1. 观察者接口
interface Observer {
void update();
}
// 2. 被观察者接口
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 3. 具体被观察者实现
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() { return temperature; }
public float getHumidity() { return humidity; }
public float getPressure() { return pressure; }
public void setMeasurements(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers(); // 数据变化时通知所有观察者
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
// 4. 具体观察者实现
class CurrentConditionsDisplay implements Observer {
private WeatherStation weatherStation;
public CurrentConditionsDisplay(WeatherStation weatherStation) {
this.weatherStation = weatherStation;
weatherStation.attach(this);
}
@Override
public void update() {
display();
}
public void display() {
System.out.println("当前条件: 温度 " + weatherStation.getTemperature()
+ "°C, 湿度 " + weatherStation.getHumidity() + "%");
}
}
class StatisticsDisplay implements Observer {
private WeatherStation weatherStation;
private List<Float> temperatures = new ArrayList<>();
public StatisticsDisplay(WeatherStation weatherStation) {
this.weatherStation = weatherStation;
weatherStation.attach(this);
}
@Override
public void update() {
temperatures.add(weatherStation.getTemperature());
display();
}
public void display() {
float avg = (float) temperatures.stream()
.mapToDouble(Float::doubleValue)
.average()
.orElse(0.0);
System.out.println("平均温度: " + avg + "°C");
}
}
2.2 推模型 vs 拉模型实现
java
java
java
下载
复制
// 推模型 - 被观察者主动推送数据
interface PushObserver {
void update(float temp, float humidity, float pressure);
}
// 拉模型 - 观察者根据需要从被观察者拉取数据
interface PullObserver {
void update(Subject subject);
}
// Java内置Observable实现(已过时,了解即可)
import java.util.Observable;
import java.util.Observer;
class WeatherDataOld extends Observable {
private float temperature;
public void measurementsChanged() {
setChanged(); // 必须调用
notifyObservers(); // 不传参数为拉模型
// notifyObservers(temperature); // 传参数为推模型
}
}
2.3 线程安全实现
java
java
java
下载
复制
// 线程安全观察者模式
class ThreadSafeSubject implements Subject {
private final List<Observer> observers = new CopyOnWriteArrayList<>();
private final Object lock = new Object();
private volatile int state;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
public void setState(int newState) {
synchronized(lock) {
this.state = newState;
}
notifyObservers();
}
}
三、应用场景分析
3.1 GUI框架中的事件监听
less
java
java
下载
复制
// Java Swing中的观察者模式
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 观察者响应事件
}
});
3.2 消息中间件/事件总线
typescript
java
java
下载
复制
// 简单事件总线实现
class EventBus {
private Map<Class<?>, List<Consumer<Object>>> handlers = new ConcurrentHashMap<>();
public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
handlers.computeIfAbsent(eventType, k -> new ArrayList<>())
.add((Consumer<Object>) handler);
}
public <T> void publish(T event) {
List<Consumer<Object>> eventHandlers = handlers.get(event.getClass());
if (eventHandlers != null) {
eventHandlers.forEach(handler -> handler.accept(event));
}
}
}
3.3 实际应用场景
- MVC架构 - Model作为Subject,View作为Observer
- 配置管理 - 配置变更通知所有使用配置的组件
- 股票交易系统 - 股价变化通知所有相关方
- 发布-订阅系统 - 消息队列、Kafka等
- Reactive编程 - RxJava、Reactor等响应式框架
四、优缺点深度分析
4.1 优点
✅ 松耦合:观察者和被观察者之间依赖抽象,不依赖具体实现
✅ 开放封闭原则:易于增加新的观察者
✅ 广播通信:一对多通知效率高
✅ 运行时建立关系:观察关系可动态变更
4.2 缺点
❌ 通知顺序不确定:观察者接收通知的顺序可能不可控
❌ 内存泄漏风险:忘记移除观察者会导致对象无法回收
❌ 性能问题:观察者过多时通知耗时
❌ 循环依赖:观察者中又更新被观察者可能引发递归
❌ 更新细节不明确:观察者可能不知道具体什么发生了变化
五、改进与优化方向
5.1 异步通知优化
java
java
java
下载
复制
// 异步观察者模式
class AsyncSubject implements Subject {
private final ExecutorService executor = Executors.newFixedThreadPool(4);
private final List<Observer> observers = new ArrayList<>();
@Override
public void notifyObservers() {
for (Observer observer : observers) {
executor.submit(() -> {
try {
observer.update();
} catch (Exception e) {
// 错误处理
}
});
}
}
}
5.2 观察者优先级
php
java
java
下载
复制
// 带优先级的观察者模式
class PrioritySubject implements Subject {
private final PriorityQueue<PriorityObserver> observers =
new PriorityQueue<>(Comparator.comparingInt(PriorityObserver::getPriority).reversed());
class PriorityObserver {
Observer observer;
int priority;
// getters, setters
}
}
5.3 弱引用避免内存泄漏
java
java
java
下载
复制
// 使用弱引用包装观察者
class WeakRefSubject implements Subject {
private final List<WeakReference<Observer>> observers = new ArrayList<>();
@Override
public void attach(Observer observer) {
observers.add(new WeakReference<>(observer));
}
@Override
public void notifyObservers() {
Iterator<WeakReference<Observer>> it = observers.iterator();
while (it.hasNext()) {
Observer observer = it.next().get();
if (observer == null) {
it.remove(); // 清理已被GC的观察者
} else {
observer.update();
}
}
}
}
六、注意事项与最佳实践
6.1 线程安全注意事项
- 在并发环境下使用
CopyOnWriteArrayList或同步机制 - 避免在通知过程中修改观察者列表
- 考虑使用读写锁优化性能
6.2 性能优化建议
ini
java
java
下载
复制
// 批量更新优化
class BatchUpdateSubject implements Subject {
private boolean isNotifying = false;
private boolean dirty = false;
public void setState(Object newState) {
this.state = newState;
dirty = true;
if (!isNotifying) {
notifyObservers();
}
}
@Override
public void notifyObservers() {
if (!dirty) return;
isNotifying = true;
dirty = false;
// ... 通知逻辑
isNotifying = false;
if (dirty) { // 通知期间又有新变化
notifyObservers();
}
}
}
6.3 避免常见陷阱
- 在update()中修改Subject → 可能导致递归调用栈溢出
- 长耗时update() → 影响其他观察者,考虑异步
- 忘记detach() → 内存泄漏,使用弱引用或自动清理
- update()抛出异常 → 影响后续观察者,需要异常处理
七、与其他模式的关系
7.1 与发布-订阅模式区别
| 观察者模式 | 发布-订阅模式 |
|---|---|
| 直接通信 | 通过中间件通信 |
| 耦合度高 | 完全解耦 |
| 同步调用 | 支持异步 |
| 简单轻量 | 功能更强大 |
7.2 与中介者模式结合
typescript
java
java
下载
复制
// 观察者+中介者混合模式
class EventMediator {
private Map<String, List<Observer>> topicObservers = new HashMap<>();
public void subscribe(String topic, Observer observer) {
topicObservers.computeIfAbsent(topic, k -> new ArrayList<>())
.add(observer);
}
public void publish(String topic, Object data) {
// 中介者负责路由消息
}
}
八、实际应用示例
8.1 Spring框架中的观察者
typescript
java
java
下载
复制
// Spring事件机制
@Component
class WeatherService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
public void setTemperature(float temp) {
publisher.publishEvent(new TemperatureChangeEvent(this, temp));
}
}
@Component
class TemperatureDisplay implements ApplicationListener<TemperatureChangeEvent> {
@Override
public void onApplicationEvent(TemperatureChangeEvent event) {
System.out.println("温度变为: " + event.getTemperature());
}
}
8.2 Android中的观察者模式
scala
java
java
下载
复制
// LiveData观察者
public class WeatherViewModel extends ViewModel {
private MutableLiveData<Float> temperature = new MutableLiveData<>();
public LiveData<Float> getTemperature() {
return temperature;
}
}
// Activity中观察
viewModel.getTemperature().observe(this, newTemp -> {
// UI更新
});
总结
观察者模式是一种极其重要的行为型设计模式,在事件驱动架构、响应式编程、UI框架等领域有广泛应用。正确使用时能够实现高度解耦的系统架构,但需要注意线程安全、内存管理、性能优化等实际问题。在现代开发中,通常使用成熟的框架实现(如Spring事件、RxJava等),而非手动实现,但深入理解其原理对于设计高质量软件至关重要。
关键建议:
- 小型系统可手动实现简单观察者模式
- 企业级应用推荐使用成熟的事件框架
- 注意观察者的生命周期管理
- 考虑异步处理避免阻塞
- 在分布式场景下考虑消息中间件替代
**