Head First设计模式读书笔记二 观察者模式

本文示例代码材料源自Head First设计模式
以前整理自己整理的观察者模式的链接:
https://blog.csdn.net/u011109881/article/details/59773041

思想

观察者模式是使用的比较普遍的设计模式,其核心思想是在被观察者(Observable/Subject)中放入观察者(Observer)的实例列表,一旦被观察者有数据更新,则遍历观察者列表,调用观察者方法来更新数据。

下面举个例子:比如一家气象站有气象数据,拥有历史 当前和未来数天的天气数据,现在要将这些数据显示到天气布告板上去,但是,布告板有详细的布告板也有简单的布告板,总之,数据可能给布告板的都是相同,但是布告板可能显示成不同的样子,或者布告板只取它需要的数据显示。这个例子可以用观察者模式来实现。

示例思路(规划类图)

这里写图片描述
UML结构图
这里写图片描述

实际代码

Bean

public class WeatherBean {
    double temperature;
    double humidity;
    double pressure;
    public double getTemperature() {
        return temperature;
    }
    public void setTemperature(double temperature) {
        this.temperature = temperature;
    }
    public double getHumidity() {
        return humidity;
    }
    public void setHumidity(double humidity) {
        this.humidity = humidity;
    }
    public double getPressure() {
        return pressure;
    }
    public void setPressure(double pressure) {
        this.pressure = pressure;
    }

}

观察者的接口

public interface DisplayElement {
    public void display(WeatherBean data);
}

观察者和其实现类

public interface Observer {
    public void update(WeatherBean data);
}
public class OneObserverDisplay implements Observer, DisplayElement {
    public WeatherStation mStation = null;
    public OneObserverDisplay(WeatherStation station){
        this.mStation = station;
        mStation.registerObserver(this);
    }

    public void display(WeatherBean data) {
        System.out.println("this is " + this.getClass().getName()
                + " getHumidity:" + data.getHumidity() + " getPressure:"
                + data.getPressure() + " getTemperature:"
                + data.getTemperature());
    }

    public void removeListener(){
        this.mStation.removeObserver(this);
    }

    public void update(WeatherBean data) {
        display(data);
    }

}
public class TwoObserverDisplay implements Observer, DisplayElement {
    public WeatherStation mStation = null;
    public TwoObserverDisplay(WeatherStation station){
        this.mStation = station;
        mStation.registerObserver(this);
    }

    public void display(WeatherBean data) {
        System.out.println("this is " + this.getClass().getName()
                + " getHumidity:" + data.getHumidity() + " getPressure:"
                + data.getPressure() + " getTemperature:"
                + data.getTemperature());
    }

    public void removeListener(){
        this.mStation.removeObserver(this);
    }

    public void update(WeatherBean data) {
        display(data);
    }

}

ThreeObserverDisplay就不贴了,基本一样
被观察者和其实现类

public interface Subject {
    void registerObserver(Observer ob);
    void removeObserver(Observer ob);
    void notifyObservers();
}
public class WeatherStation implements Subject{
    private WeatherBean mWeatherBean;
    //API 1.5
    private ArrayList<Observer> mObservers = new ArrayList<Observer>();
    public void setMeasurement(WeatherBean data){
        mWeatherBean = data;
        measurementChanged();
    }
    private void measurementChanged(){
        notifyObservers();
    }
    public void registerObserver(Observer ob) {
        if(!mObservers.contains(ob)){
            mObservers.add(ob);
        }
    }
    public void removeObserver(Observer ob) {
        if(mObservers.contains(ob)){
            mObservers.remove(ob);
        }
    }
    public void notifyObservers() {
        for(Observer observer : mObservers){
            observer.update(mWeatherBean);
        }
    }
}

测试类

public class TestObserver {

    public static void main(String[] args) {
        WeatherStation station = new WeatherStation();
        OneObserverDisplay one = new OneObserverDisplay(station);
        TwoObserverDisplay two = new TwoObserverDisplay(station);
        ThreeObserverDisplay three = new ThreeObserverDisplay(station);
        WeatherBean data = new WeatherBean();
        data.setHumidity(1.1);
        data.setPressure(1.2);
        data.setTemperature(1.3);
        station.setMeasurement(data);
        one.removeListener();
        station.setMeasurement(data);
    }
}

测试结果:

this is observer.OneObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3

使用Java API再写观察者的例子

实际上,Java API是支持观察者的,即上面例子中的Subject和Observer Java API中已经有所定义,即util包中的Observerable和Observer。那么我们可以不用写Subject和Observer了
上述例子使用Java API写出的效果是什么样子呢?我的实现是这样的:
先UML图就不画了,类截图如下:
这里写图片描述
写法比之前简化了不少
对比之前的类的截图,可以发现Subject和Observer不要写了,因为API已经写好了。
其次WeatherStation中要删掉无用代码,很多代码在Observable中已经帮我们写好了,废代码我在下面贴出的代码中标出了
最后各个Display的removeListener和update方法需要修改,因为Observer已经不是我们写的Observer,而是Java API中定义好的接口,我们要按接口来实现方法
剩下的都一样了,怎么样转变是不是很灵活。
我贴一下改动的部分,没有改动的就不贴了。

首先是被观察者实现类

public class WeatherStation extends Observable{
    private WeatherBean mWeatherBean = null;
    //private ArrayList<Observer> mObservers = new ArrayList<Observer>();//不需要了,Observable维护了一个Vector<Observer>数组
    public void setMeasurement(WeatherBean data){
        mWeatherBean = data;
        measurementChanged();
    }
    private void measurementChanged(){
        setChanged();
        notifyObservers(mWeatherBean);
    }

/*  
    不再需要自己写deleteObserver和registerObserver方法
    Observable中addObserver(Observer ob) 和removeObserver(Observer ob)是现成的 
    @Override
    public void deleteObserver(Observer ob) {
        if(mObservers.contains(ob)){
            mObservers.remove(ob);
        }
    }

    public void registerObserver(Observer ob) {
        if(!mObservers.contains(ob)){
            mObservers.add(ob);
        }
    }*/
    /*
     notifyObservers也不需要了,Observable的notifyObservers也写好了
    @Override
    public void notifyObservers() {
        super.notifyObservers();
        for(Observer observer : mObservers){
            observer.update(mWeatherBean);
        }
    }*/
}

其次是各个观察者,我贴两个

public class OneObserverDisplay implements Observer, DisplayElement {// 此处的Observer变成了java.util.Observer;
    public WeatherStation mStation = null;

    public OneObserverDisplay(WeatherStation station) {
        this.mStation = station;
        mStation.addObserver(this);
    }

    public void display(WeatherBean data) {
        System.out.println("this is " + this.getClass().getName()
                + " getHumidity:" + data.getHumidity() + " getPressure:"
                + data.getPressure() + " getTemperature:"
                + data.getTemperature());
    }

    public void removeListener() {
        this.mStation.deleteObserver(this);
    }

    public void update(Observable o, Object arg) {
        if (arg instanceof WeatherBean) {
            display((WeatherBean) arg);
        }
    }
}
public class TwoObserverDisplay implements Observer, DisplayElement {
    public WeatherStation mStation = null;

    public TwoObserverDisplay(WeatherStation station) {
        this.mStation = station;
        mStation.addObserver(this);
    }

    public void display(WeatherBean data) {
        System.out.println("this is " + this.getClass().getName()
                + " getHumidity:" + data.getHumidity() + " getPressure:"
                + data.getPressure() + " getTemperature:"
                + data.getTemperature());
    }

    public void removeListener() {
        this.mStation.deleteObserver(this);
    }

    public void update(Observable o, Object arg) {
        if (arg instanceof WeatherBean) {
            display((WeatherBean) arg);
        }
    }

}

剩下的代码和原来一样了。
输出结果如下:

this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.OneObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.ThreeObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3
this is observer.TwoObserverDisplay getHumidity:1.1 getPressure:1.2 getTemperature:1.3

两种情况对比

1.代码简洁
从代码简洁性上看,实现Java API的方式完胜,省略了很多代码。
2.输出顺序
对比输出可以看出,输出顺序略微不同,可以看下被观察者的notifyObservers的实现:

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

可以看出,这个是倒序通知的。
3.setChange
API中的通知,有个private boolean changed = false;常量,在通知前,会判断其值,如果其是false是不会通知的。因此,每次通知或者数据变化都需要调用setChanged来确保发出通知。
4.变化适应性
其实Java API的设计有点问题,在Java编程经验中有一条:要面向接口编程而不是面向实现。
我们发现原先我们自己写的Subject是个接口,而JAVA API中的Observable是个实体类,这样如果我们的被观察者继承了Observable就无法继承其他类了,这就限制了被观察者实现的实体类的扩展和复用。
另外Java编程提倡多用组合少用继承。但是Observable将changed的控制方法大多设置为protected,这就意味着,即使我们把Observable的实体类组合到Observer实体类中,也无法控制changed因为protected只在本类及其子类可用。这违反了用组合少用继承,使得程序不够灵活,有时会限制程序的扩展。

总结,再看实例

因此,JAVA API的观察者模式的实现虽然减少了不少代码,但是也存在诸多扩展和灵活性问题。如果JAVA API提供的方式已经够用,那么就可以使用简便的JAVA API方式,但是如果觉得程序后期需要扩展的可能性大,那么最好还是不要闲麻烦,自己实现一下。对比之前的总结,我觉得还是当前这个版本比较通俗易懂。

在Java和Android系统中,观察者模式的例子很常见,比如Swing组件和Android Button控件的点击事件监听。Android 的广播和广播接收者,也是利用注册和解除注册来实现接收广播的,至少形式上很相似。
这就像从报社订报纸,每个用户可以选择定或者不定报纸。报社每月会遍历订阅用户列表,给他们寄去报纸;如果用户中途取消订阅,报社就删除该用户,下次不再寄去报纸。其他没有订阅的用户如果感兴趣了,也可以订阅,报社会在列表加入该用户,下次寄去报纸给该用户。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值