观察者模式的具体实现

气象站(WeatherStation) 是被观察者(Subject),它会持续监测天气数据(温度、湿度、气压)。当数据发生变化时,它需要通知所有订阅了它的显示器(Display),比如当前 conditions 显示器统计数据显示器天气预报显示器。这些显示器就是观察者(Observer)。

步骤 1:定义被观察者(Subject)接口

这个接口定义了被观察者需要具备的核心能力:注册观察者、移除观察者、通知观察者。



import java.util.List;
 
/**
 * 被观察者(Subject)接口
 */
public interface Subject {
    /**
     * 注册一个观察者
     * @param observer 要注册的观察者
     */
    void registerObserver(Observer observer);
 
    /**
     * 移除一个观察者
     * @param observer 要移除的观察者
     */
    void removeObserver(Observer observer);
 
    /**
     * 当主题状态改变时,通知所有注册的观察者
     */
    void notifyObservers();
}

步骤 2:定义观察者(Observer)接口

这个接口定义了观察者的核心能力:接收并处理来自被观察者的通知。

java

运行



/**
 * 观察者(Observer)接口
 */
public interface Observer {
    /**
     * 当被观察者状态改变时,会调用此方法通知观察者
     * @param temperature 温度
     * @param humidity 湿度
     * @param pressure 气压
     */
    void update(float temperature, float humidity, float pressure);
}

步骤 3:定义显示器(DisplayElement)接口 (可选,但推荐)

为了统一所有显示器的展示行为,我们可以创建一个 
DisplayElement
 接口。

java

运行



/**
 * 显示器接口,所有显示器都应实现此接口
 */
public interface DisplayElement {
    /**
     * 展示当前数据
     */
    void display();
}

步骤 4:实现具体的被观察者(ConcreteSubject)

这里我们实现 
WeatherStation
 类,它负责管理天气数据和观察者列表。

java

运行



import java.util.ArrayList;
import java.util.List;
 
/**
 * 具体的被观察者:气象站
 */
public class WeatherStation implements Subject {
    // 存储所有注册的观察者
    private List<Observer> observers;
    
    // 气象数据
    private float temperature;
    private float humidity;
    private float pressure;
 
    public WeatherStation() {
        // 初始化观察者列表
        observers = new ArrayList<>();
    }
 
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
 
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
 
    @Override
    public void notifyObservers() {
        // 遍历所有观察者,并调用它们的 update 方法
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
 
    /**
     * 当气象站检测到新数据时,调用此方法
     * @param temperature 温度
     * @param humidity 湿度
     * @param pressure 气压
     */
    public void measurementsChanged(float temperature, float humidity, float pressure) {
        System.out.println("
WeatherStation: Measurements updated. Notifying observers...");
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        
        // 数据改变,通知所有观察者
        notifyObservers();
    }
    
    // 模拟气象站获取数据的方法
    public void setMeasurements(float temperature, float humidity, float pressure) {
        measurementsChanged(temperature, humidity, pressure);
    }
}

步骤 5:实现具体的观察者(ConcreteObserver)

现在我们来实现几个具体的显示器。

1. 当前 conditions 显示器

java

运行



/**
 * 具体的观察者:当前天气状况显示器
 */
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    
    // 可选:持有一个对 Subject 的引用,以便后续可以取消订阅
    private Subject weatherStation;
 
    public CurrentConditionsDisplay(Subject weatherStation) {
        this.weatherStation = weatherStation;
        // 在构造函数中自动注册自己为观察者
        weatherStation.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.println("Current Conditions Display: " + temperature + "F degrees and " + humidity + "% humidity");
    }
}
2. 统计数据显示器

java

运行



/**
 * 具体的观察者:统计数据显示器
 */
public class StatisticsDisplay implements Observer, DisplayElement {
    private float maxTemp = 0.0f;
    private float minTemp = 200;
    private float tempSum = 0.0f;
    private int numReadings;
 
    public StatisticsDisplay(Subject weatherStation) {
        weatherStation.registerObserver(this);
    }
 
    @Override
    public void update(float temperature, float humidity, float pressure) {
        tempSum += temperature;
        numReadings++;
 
        if (temperature > maxTemp) {
            maxTemp = temperature;
        }
 
        if (temperature < minTemp) {
            minTemp = temperature;
        }
 
        display();
    }
 
    @Override
    public void display() {
        System.out.println("Statistics Display: Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);
    }
}
3. 天气预报显示器

java

运行



/**
 * 具体的观察者:天气预报显示器
 */
public class ForecastDisplay implements Observer, DisplayElement {
    private float currentPressure = 29.92f;
    private float lastPressure;
 
    public ForecastDisplay(Subject weatherStation) {
        weatherStation.registerObserver(this);
    }
 
    @Override
    public void update(float temperature, float humidity, float pressure) {
        lastPressure = currentPressure;
        currentPressure = pressure;
 
        display();
    }
 
    @Override
    public void display() {
        System.out.print("Forecast Display: ");
        if (currentPressure > lastPressure) {
            System.out.println("Improving weather on the way!");
        } else if (currentPressure == lastPressure) {
            System.out.println("More of the same.");
        } else if (currentPressure < lastPressure) {
            System.out.println("Watch out for cooler, rainy weather.");
        }
    }
}

步骤 6:客户端代码(使用观察者模式)

最后,我们来创建一个 
Main
 类来演示整个系统的工作流程。

java

运行



/**
 * 客户端应用
 */
public class WeatherStationDemo {
    public static void main(String[] args) {
        // 1. 创建被观察者:气象站
        WeatherStation weatherStation = new WeatherStation();
 
        // 2. 创建观察者,并将它们注册到气象站
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherStation);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherStation);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherStation);
 
        // 3. 模拟气象站数据更新
        System.out.println("--- First measurement ---");
        weatherStation.setMeasurements(80, 65, 30.4f);
 
        System.out.println("
--- Second measurement ---");
        weatherStation.setMeasurements(82, 70, 29.2f);
 
        System.out.println("
--- Third measurement ---");
        weatherStation.setMeasurements(78, 90, 29.2f);
        
        // 4. 演示如何移除一个观察者
        System.out.println("
--- Removing StatisticsDisplay ---");
        weatherStation.removeObserver(statisticsDisplay);
        
        System.out.println("
--- Fourth measurement ---");
        weatherStation.setMeasurements(62, 95, 28.1f);
    }
}

运行结果

plaintext



--- First measurement ---
WeatherStation: Measurements updated. Notifying observers...
Current Conditions Display: 80.0F degrees and 65.0% humidity
Statistics Display: Avg/Max/Min temperature = 80.0/80.0/80.0
Forecast Display: Improving weather on the way!
 
--- Second measurement ---
WeatherStation: Measurements updated. Notifying observers...
Current Conditions Display: 82.0F degrees and 70.0% humidity
Statistics Display: Avg/Max/Min temperature = 81.0/82.0/80.0
Forecast Display: Watch out for cooler, rainy weather.
 
--- Third measurement ---
WeatherStation: Measurements updated. Notifying observers...
Current Conditions Display: 78.0F degrees and 90.0% humidity
Statistics Display: Avg/Max/Min temperature = 80.0/82.0/78.0
Forecast Display: More of the same.
 
--- Removing StatisticsDisplay ---
 
--- Fourth measurement ---
WeatherStation: Measurements updated. Notifying observers...
Current Conditions Display: 62.0F degrees and 95.0% humidity
Forecast Display: Watch out for cooler, rainy weather.

总结

通过这个例子,我们可以清晰地看到观察者模式的几个关键部分:

松耦合
WeatherStation
 不知道具体的观察者是谁(
CurrentConditionsDisplay

StatisticsDisplay
 等),它只知道它们实现了 
Observer
 接口。同样,观察者也不知道被观察者的内部细节,只关心它收到的数据。可扩展性:如果我们想添加一个新的显示器(比如 “紫外线指数显示器”),只需要创建一个新的类实现 
Observer
 和 
DisplayElement
 接口,然后在 
WeatherStation
 中注册即可。不需要修改任何现有代码。动态关系:观察者可以在任何时候注册或取消注册,这种关系是动态的。

JDK 中也内置了观察者模式的支持,即 
java.util.Observable
 类和 
java.util.Observer
 接口。不过,上面我们手动实现的方式更有助于理解其核心思想。在现代 Java 开发中,更推荐使用如 Google Guava 的 
EventBus
 或 Spring 的 
ApplicationEvent
 等事件驱动模型,它们提供了更强大和灵活的观察者模式实现。

© 版权声明

相关文章

暂无评论

none
暂无评论...