2Head First Design Patterns(第2版)

观察者模式

事件驱动

观察者模式松耦合发布-订阅通知机制

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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 简化?

如何实现异步的观察者通知?

观察者模式有哪些已知的陷阱?


2/12观察者模
0%