设计模式之观察者模式

观察者模式

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

举例

  现在有一个气象站,需要发布发布天气,气象站只提供数据,需要编写程序将天气发布给每一个用户,用户接收也可能有多种模式,有的是当前天气状况也有的是天气预报。

  这时候就可以使用观察者模式,进行一对多推送,观察者模式主要就是订阅、发布过程,用户订阅了天气信息,气象站对用户及时发布最新的天气信息,用户也可以随时取消订阅。

设计模式之观察者模式

  如图

  • Subject接口: 主题接口,也就是被观察对象的接口
  • Observer接口: 观察者接口
  • DisplayElement接口: 展示接口
  • WeatherData: 具体的被观察对象
  • CurrentConditionDisplay: 当前天气状态的展示

  通过上图,就可以看出观察者模式的运行情况了,具体主题实现Subject接口,具体展示实现Observer和DisplayElement接口,Subject中有注册、移除、通知三个方法,这样就可以对观察者进行注册移除,并通知观察者。

// 模拟天气站,发布天气
public class WeatherStation
{
    public static void main(String[] args)
    {
        WeatherData weatherData = new WeatherData();

        CurrentConditionDisplay current = new CurrentConditionDisplay(weatherData);

        weatherData.setMeasurement(35, 65, 30.4f);
        weatherData.setMeasurement(36, 67, 30.5f);
        weatherData.setMeasurement(34, 66, 30.5f);

        // 移除观察者
        current.remove();
        weatherData.setMeasurement(33, 62, 30.5f);
    }
}

输出结果

当前状态: 温度 35.0度 湿度 65.0%
当前状态: 温度 36.0度 湿度 67.0%
当前状态: 温度 34.0度 湿度 66.0%

  这样就基本完成了气象站的需求,可以对观察者们发布最新的天气信息。

Java Jdk中内置的观察者模式实现

  • Observable类: 对应上述的Subject,也就是被观察的对象要实现的接口
  • Observer接口: 对应上述的Observer接口,观察者实现的接口

  使用内置的对象来实现观察者模式整体上跟上述基本相同,但是Observable是一个类,是需要用继承方式来扩展。

  在Observable中,有一个setChanged()方法,在通知之前,需要调用这个方法,来表明状态已经改变了。

  在Observer中的update()方法有两个参数,一个是一个Observable对象,还有一个就是Observable对象“推”的一个对象。在jdk内置方法中,发布有两种形式,一种是"推",一种是"拉",在Observable类中的notifyObservers()方法里,可以带一个参数,代表是"推",推送给每一个观察者,也可以不带参数,就默认是"拉"方式,这样就变得更为灵活。

  但是Observable是一个类,而不是一个接口,这样在程序扩展性上也有了一些限制,如果可以满足需求,就可以直接使用,如果不行,那么只能自己写一套观察者模式出来,就像最前面那样。

使用jdk中观察者的类

import java.util.Observable;

// 使用jdk提供的内置对象完成观察者模式
public class WeatherData extends Observable
{
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){}

    public void measurementsChanged()
    {
        setChanged();
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    // 使用“拉”模式的时候,观察者通过这些方法来获取状态
    public float getTemperature()
    {
        return temperature;
    }

    public float getHumidity()
    {
        return humidity;
    }

    public float getPressure()
    {
        return pressure;
    }
}
import ObserverPattern.DisplayElement;

import java.util.Observable;
import java.util.Observer;

// 展示当前状态
public class CurrentConditionDisplay implements Observer, DisplayElement
{
    Observable observable;
    private float temperature;
    private float humidity;

    public CurrentConditionDisplay(Observable observable)
    {
        this.observable = observable;
        observable.addObserver(this);
    }

    public void remove()
    {
        observable.deleteObserver(this);
    }

    public void update(Observable obs, Object arg)
    {
        if(obs instanceof WeatherData)
        {
            WeatherData weatherData = (WeatherData)obs;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }

    public void display()
    {
        System.out.println("当前状态: 温度 " + temperature + "度 " + " 湿度 " + humidity + "%");
    }
}

  以上具体代码都可以在我的github中查看
  地址:https://github.com/yangliu0/DesignPatterns