设计模式 —— 观察者模式

一、前言

本文根据《Head first 设计模式》总结。

二、观察者模式引入

我们假设有这样的场景:

公司和某个气象台达成了合作关系,气象台为公司提供数据服务,并且他们打算为我们提供一个WeatherData对象来实现数据服务。他们主要提供的数据服务有实时天气、天气预测、气象分析等。现在我们打算根据他们提供的数据制作几个展板,这几个展板分别用来展示实时天气、天气预测、气象分析。

公司的程序猿小张得到需求后很快便给出了相应的代码实现:

public void onDataChange(){
    humidity = getHumidity();
    pressure = getPressure();
    temperature = getTemperature();
    currentInfo.update(temperature,humidity,pressure);
    statisticsInfo.update(temperature,humidity,pressure);
    forecastInfo.update(temperature,humidity,pressure);
}

需求确实很顺利的实现了,但是很快老板又要求小张添加新的展板用来展示其它的信息。于是小张又开始重复上面的代码。

很快,小张就厌倦了这种方式,并试图解决程序中的问题。那么,小张的程序中有哪些问题呢?

1、面向具体实现编程

所谓面向具体实现编程就是说代码只局限在具体场景中,没有通用性,如果添加新的需求很难去维护。

2、代码封装性不够

比如几个对象中都含有update方法,这时就可以考虑抽离出公共接口

三、认识观察者模式

在网络不太普及的年代,人们主要通过订阅报纸来获取外部消息。订阅报纸以后我们就无需关注报纸何时更新的相关问题。等到报纸更新以后报社会第一时间通知我们。观察者模式也是同样的道理。

当观察者注册到被观察者中以后,观察者就无需关注被观察者的最新状态,等都被观察者觉察到变化后就会通知观察者去更新最新的数据。

3.1 一对多依赖

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会接收到通知并自动更新。

在这里插入图片描述

3.2 松耦合

当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。

对于观察者,主题只知道它实现了某一个接口,但是并不清楚观察者具体的实现细节。无论任何时候,我们都可以向主题中添加或移除一个观察者。因为主题只是依赖一个实现Observer接口的对象列表。无论我们添加或是删除某个观察者都不会影响到主题的运行。而且即使有新的观察者出现,主题也不需要修改任何代码,我们只需要向主题中添加观察者。主题不在乎别的,他只会发送通知给所有实现了观察者接口的对象。

如果我们在其它地方使用主题或者观察者,可以轻松的复用,因为二者不是紧耦合的。

设计原则:为了交互对象之间的松耦合设计而努力。

四、UML类图

在这里插入图片描述

五、实现观察者模式

5.1 接口设计

主题接口:

public interface Subject {
    //注册观察者
    void registerObserver(Observer observer);
    //移除观察者
    void removeObserver(Observer observer);
    //通知观察者
    void notifyObservers();
}

观察者:

public interface Observer {
    //更新数据
    void update();
}

公共显示接口:

public interface DisplayElement {
    void display();
}

5.2 类设计

WeatherData.java

public class WeatherData implements Subject{
    //观察者列表
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    private List<Float> forecastTemperatures;
    
    public WeatherData(){
        //初始化观察者列表
        observers = new ArrayList<>();
    }
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
    //当调用这个方法时,会通知所有观察者
    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity,
                                float pressure, List<Float> forecastTemperatures) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        this.forecastTemperatures = forecastTemperatures;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }

    public List<Float> getForecastTemperatures() {
        return forecastTemperatures;
    }

}

CurrentConditionsDisplay.java

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private WeatherData weatherData;
    private float temperature;//温度
    private float humidity;//湿度
    private float pressure;//气压

    public CurrentConditionsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        //注册观察者
        this.weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("当前温度为:" + this.temperature + "℃");
        System.out.println("当前湿度为:" + this.humidity);
        System.out.println("当前气压为:" + this.pressure);
    }

    @Override
    public void update() {
        this.temperature = this.weatherData.getTemperature();
        this.humidity = this.weatherData.getHumidity();
        this.pressure = this.weatherData.getPressure();
        display();
    }
}

5.3 测试

public class ObserverPatternTest {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(22f, 0.8f, 1.2f, null);
    }
}
原文地址:https://www.cnblogs.com/zwscode/p/14284082.html