Head First 设计模式(二)观察者模式

观察者模式

定义

首先看下观察者模式的定义

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

按我自己的理解翻译就是:

观察者模式就是一个主题和多个依赖者的关系,当主题发生变化时,依赖者得到通知发生对应的变化。用气象站的例子解释:当气象站(主题)检测到天气情况的改变时,会通知不同的显示器(依赖者)显示出天气的情况。同时,由于显示器自身的不同,显现信息也不一样,如气压显示器显示气压,湿度显示器显示湿度。

代码

我们以定义中的气象站为例。

框架

首先给出一个大概的框架:

主要分为了两个接口:主题接口依赖者(Observer,也称观察者)接口

  1. 主题类中组合了它的依赖者们,所以接口定义了增加依赖者registerObserver()、删除依赖者removeObserver()、以及主题发生变化时通知依赖者变化的方法notifyObservers()

  2. 依赖者接口定义了专门给主题类调用的方法update(),主题类通过调用依赖类的该方法来改变依赖类。另外还有一个display()方法来显示出依赖类的改变

详细代码

接下来我们用具体的代码来说明。代码中的主题和依赖者,分别继承和实现了Java.utilObservableObserver,这是Java中封装好的观察者模式类,有兴趣的可以看下源码。

为了更清晰了解观察者模式,我覆盖了父类的实现。

主题类

值得注意的地方是,主题通知依赖类,有两种方式:推和拉。

推就是把固定的数据传给依赖类的方法,拉就是把主题传给依赖类,依赖类自己调主题的方法来获取数据。

/**
 * 气象站(观察者模式中的主题)
 */
public class WeatherData extends Observable{
    /**温度*/
    private float temperature;
    /**湿度*/
    private float humidity;
    /**气压*/
    private float pressure;

    /**该主题的依赖者集合*/
    private Vector<Observer> observers = new Vector<>();
    /**主题改变标志,用于控制 主题的改变触发依赖类的改变的条件*/
    private Boolean isChange = false;

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }

    /**
     * 增加观察者
     */
    @Override
    public synchronized void addObserver(Observer observer){
        observers.addElement(observer);
    }

    /**
     * 删除观察者
     */
    @Override
    public synchronized void deleteObserver(Observer observer){
        observers.addElement(observer);
    }

    /**
     * 通知观察者(拉)
     */
    @Override
    public void notifyObservers(){
        notifyObservers(null);
    }

    /**
     * 通知观察者(推)
     */
    @Override
    public synchronized void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!isChange)
                return;
            arrLocal = observers.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    @Override
    public void setChanged() {
        isChange = true;
    }

    /**
     * 每次数据改动后调用的方法
     */
    public void measurementsChanged(){
        setChanged();
        //将主题传给依赖类,依赖类调用主题的方法自己"拉取"数据
        notifyObservers();
        //将固定的数据"推"给依赖类,依赖类只能获取指定的数据
      /*Map<String, Float> args = new HashMap<>();
        args.put("temperature", temperature);
        args.put("humidity", humidity);
        args.put("pressure", pressure);
        notifyObservers(args);*/
    }

    /**
     * 模拟数据改动
     */
    public void setMeasurements(float temp, float humidity, float pressure){
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

}

依赖类(观察者类)

由于他们都要显示出气象站的数据,所以定义了一个公共展示接口

public interface Display {
    public void display();
}

第一个依赖类,假设只显示气压和温度

/**
 * 依赖类,当气象站数据发生改变时,显示数据
 */
public class StatisticsDisplay implements Observer,Display{
    /**主题*/
    private Observable observable;
    private float temperature;
    private float pressure;

    public StatisticsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }
    @Override
    public void update(Observable o, Object arg) {
        //推的方式获取数据
        /*if (arg instanceof HashMap) {
            HashMap<String, Float> args = (HashMap<String, Float>) arg;
            this.temperature = args.get("temperature");
            this.pressure = args.get("pressure");

            display();
        }*/

        //拉的方式获取数据
        if(o instanceof WeatherData){
            WeatherData weatherData = (WeatherData) o;
            this.temperature = weatherData.getTemperature();
            this.pressure = weatherData.getPressure();

            display();
        }
    }

    @Override
    public void display() {
        System.out.println("TEMP/PRESSURE:"+temperature+" /"+ pressure);
    }

}

第二个依赖类,假设只显示温度和湿度:

/**
 * 依赖类,当气象站数据发生改变时,显示数据
 */
public class CurrentConditionDisplay implements Observer,Display{
    /**主题*/
    private Observable observable;
    private float temperature;
    private float humidity;

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

    @Override
    public void update(Observable o, Object arg) {
        //推的方式获取数据
        /*if (arg instanceof HashMap) {
            HashMap<String, Float> args = (HashMap<String, Float>) arg;
            this.temperature = args.get("temperature");
            this.humidity = args.get("humidity");

            display();
        }*/

        //拉的方式获取数据
        if(o instanceof WeatherData){
            WeatherData weatherData = (WeatherData) o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();

            display();
        }
    }

    @Override
    public void display() {
        System.out.println("Current condition:"+temperature+" F degree and "+humidity +"% humidity");
    }

}

测试

public class Main {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionDisplay conditionDisplay = new CurrentConditionDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);

        weatherData.setMeasurements(80, 65, 30.4F);
        weatherData.setMeasurements(76, 79, 40.4F);
        weatherData.setMeasurements(92, 75, 20.4F);
    }
}/**output:
TEMP/PRESSURE:80.0 /30.4
Current condition:80.0 F degree and 65.0% humidity
TEMP/PRESSURE:76.0 /40.4
Current condition:76.0 F degree and 79.0% humidity
TEMP/PRESSURE:92.0 /20.4
Current condition:92.0 F degree and 75.0% humidity
**/

猜你喜欢

转载自blog.csdn.net/z55887/article/details/64904350