观察者模式

需求

监听天气数据,一旦数据改变,则更新布告板的内容。数据有:温度、湿度、气压

被观察者,也称为主题,Observable,(Subject)

/**
 * 主题接口,所有主题接口都应该实现此接口
 */
public interface Subject
{
    /**
     * 注册观察者
     */
    public void registerObserver(Observer o);

    /**
     * 删除观察者
     */
    public void removeObserver(Observer o);

    /**
     * 当主题状态改变的时候,会调用此方法,通知所有的观察者
     */
    public void notifyObservers();
}


/**
 * 处理气象站数据的类,实现了主题Subject接口,用来接收气象站的信息,并通知所有的观察者
 */
public class WeatherData implements Subject
{
    // 用来记录观察者
    private ArrayList observers = new ArrayList();

    private float temperature;
    private float humidity;
    private float pressure;

    public void registerObserver(Observer o)
    {
        observers.add(o);
    }

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

    /**
     * 把状态告诉每一个观察者,观察者们都实现了update方法,所有我们知道如何通知他们
     */
    public void notifyObservers()
    {
        for (Observer observer : observers)
        {
            observer.update(temperature, humidity, pressure);
        }
    }

    /**
     * 当气象站数据更新的时候,此方法被调用
     */
    public void measurementsChanged()
    {
        notifyObservers();
    }

    /**
     * 由于是模拟气象站,因此通过此方法模拟气象站调用measurementsChanged()方法
     */
    public void setMeasurements(float temperature, float humidity,
            float pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    // 省略 get
}

观察者(Observer)与布告板

/**
 * 观察者接口,所有观察者都应该实现此接口
 */
public interface Observer
{
    /**
     * 所有的观察者都必须实现的方法,当主题状态变化时,调用此方法
     * 
     * @param temp      温度
     * @param humidity  湿度
     * @param pressure  气压
     */
    public void update(float temp, float humidity, float pressure);
}


/**
 * 布告板接口,所有的气象布告板都需要实现此接口
 */
public interface DisplayElement
{
    /**
     * 当布告板需要显示时,调用此方法
     */
    public void display();
}


/**
 * 状况布告板
 */
public class CurrentConditionsDisplay implements Observer, DisplayElement
{
    private float temperature;
    private float humidity;

    // 之所以保留主题的引用,是为了方便以后可能要删除观察者
    private Subject weatherData;

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

    /**
     * 当主题状态变化时,调用此方法
     */
    public void update(float temperature, float humidity, float pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        // 显示布告板,设置数据的显示方式,可以有很多方式,比如MVC
        display();
    }

    /**
     * 当布告板需要显示时,调用此方法
     */
    public void display()
    {
        System.out.println("Current conditions: " + temperature
                + "F degrees and " + humidity + "% humidity");
    }
}

测试

public static void main(String[] args)
{
    // 建立气象站数据处理类对象
    WeatherData weatherData = new WeatherData();

    CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(
            weatherData);
    // StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
    // ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
    // HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);

    weatherData.setMeasurements(80, 65, 30.4f);
    weatherData.setMeasurements(82, 70, 29.2f);
    weatherData.setMeasurements(78, 90, 29.2f);
}

观察者模式的定义:

定义了对象间的一对多依赖,当一个对象状态改变时,所以依赖者都会收到通知并自动更新

设计原则 4

让交互对象松耦合,降低对象的互相依赖,彼此不知道对方的细节,但是可以交互。

观察者:Observer

被观察者:Observable,(Subject)

订阅报纸的例子

Java 也有内置的观察者模式

应用了哪些设计原则

  • 组合:观察者是通过运行时注册到被观察者的
  • 针对接口:观察者和被观察者都是实现了对应的接口,注册和通知都是接口来进行的,松耦合
  • 抽出会改变的部分,和不变的分开来:被观察者的状态会改变,观察者的数量和类型会改变,但是被观察者本身不用变,只需要关注各自状态的改变

注意

观察者被通知的次序是未知的

你可能感兴趣的:(观察者模式)