第2章:观察者模式(Observer Pattern)
一、导读
1.1 本章学习目标
第2章讲解观察者模式——一种定义对象之间一对多依赖关系的设计模式。当一个对象的状态改变时,所有依赖它的对象都会被自动通知并更新。
本章的核心学习目标包括:
- 理解观察者模式的意图:建立对象间的一对多依赖关系。
- 掌握松耦合的设计原则:主题和观察者之间的松耦合。
- 学会实现发布-订阅机制:注册、取消注册、通知。
- 理解观察者模式的适用场景:事件驱动、数据同步。
- 掌握 Java 中的观察者模式实现:使用接口和集合。
1.2 为什么第二章如此重要
观察者模式是事件驱动编程的基础。从 GUI 事件处理到响应式编程,观察者模式无处不在。理解这个模式,是理解现代软件架构的关键。
1.3 核心问题引导
观察者模式解决了什么问题?
松耦合有什么好处?
如何避免观察者和主题之间的循环依赖?
观察者模式的性能问题是什么?
Java 内置的 Observer 接口有什么问题?
二、核心概念详解
2.1 观察者模式的结构
┌──────────────┐ ┌──────────────────┐
│ Subject │ │ <<interface>> │
│──────────────│ │ Observer │
│ - observers │ │──────────────────│
│──────────────│ │ + update() │
│ + register() │ └──────────────────┘
│ + unregister()│ ▲
│ + notify() │ │
└──────────────┘ ┌─────┴──────┐
│ ConcreteObs │
│─────────────│
│ + update() │
└─────────────┘2.2 实现
java
// 主题接口
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 观察者接口
public interface Observer {
void update(float temp, float humidity, float pressure);
}
// 显示接口
public interface DisplayElement {
void display();
}
// 具体主题
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
// 具体观察者
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private final Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.printf("Current conditions: %.1f°F, %.1f%% humidity%n",
temperature, humidity);
}
}2.3 松耦合的力量
观察者模式的核心价值是松耦合:
主题不知道观察者的具体类型:只知道观察者实现了 Observer 接口。
观察者可以随时注册和取消注册:不影响主题的运行。
可以独立修改主题或观察者:只要接口不变,两者可以独立变化。
可以复用主题或观察者:它们不依赖于具体的上下文。
三、实战案例
3.1 股票行情系统
java
public class StockMarket implements Subject {
private List<Observer> observers = new ArrayList<>();
private Map<String, Double> stockPrices = new HashMap<>();
@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(stockPrices);
}
}
public void updatePrice(String symbol, double price) {
stockPrices.put(symbol, price);
notifyObservers();
}
}
public class TradingApp implements Observer {
@Override
public void update(Map<String, Double> prices) {
prices.forEach((symbol, price) ->
System.out.printf("%s: $%.2f%n", symbol, price));
}
}
public class AnalyticsDashboard implements Observer {
@Override
public void update(Map<String, Double> prices) {
double avg = prices.values().stream()
.mapToDouble(Double::doubleValue).average().orElse(0);
System.out.printf("Average price: $%.2f%n", avg);
}
}四、本章小结
观察者模式定义了对象间的一对多依赖关系,当一个对象状态改变时,所有依赖对象都会被自动通知。核心价值是松耦合——主题和观察者可以独立变化。
五、思考与练习
观察者模式和发布-订阅模式有什么区别?
如何避免观察者之间的通知顺序问题?
观察者模式在 Java 8+ 中如何用 Lambda 简化?
如何实现异步的观察者通知?
观察者模式有哪些已知的陷阱?