head first 设计模式之---------观察者模式

来源:互联网 发布:淘宝刷客收入揭秘 编辑:程序博客网 时间:2024/05/20 19:49

一、观察者模式 定义

   观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。

   这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。

如何更好 理解???
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

二、观察者模式的组成

抽象主题角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。   
具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。   
具体观察者角色:该角色实现抽象观察者角


色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

2.1*强调内容* 观察者模式原型
里写图片描述

2.2观察者实现方式

1.主题主动推送数据

当数据更新后,主题会将所有更新数据都推送给观察者,而观察者只能被动接受主题的推送信息。

2.观察者主动拉取数据

当数据更新后,主题会通知观察者数据更新了,并暴露出相关数据的getter方法,观察者们可根据自己的需要去拉取自己需要的信息。

2.3具体举例来说明:

目前我们需要实现一种天气实时更新的程序(天气推送),当气象站数据更新后,天气接口程序去获取最新天气数据,然后将数据分发给所有订阅过“天气日报”程序的用户,即使更新数据。

(案例来源于《Head First 设计模式》)

整体结构图:
这里写图片描述

由上图可以看出,气象站负责去检测天气情况,当数据发生变化时,天气服务获取了变化的数据并且需要将数据分发给众多订阅“天气推送”的用户,如果我们采用一个个去通知的形式去实现的话,那么当我们新增了一个观察者,我们又需要单独重复编写发布信息代码,当我们想去掉一个观察者的时候,又需要去删减部分代码,这样的操作实在很繁琐,耦合度也比较高。接下来我们采用观察者模式来实现这个需求,来看看有没有什么神奇的地方。

两种方式的代码实现

方式一:主题推送数据

这里我们采取自己编写抽象主题与抽象观察者代码的方式来实现。

抽象主题:SubjectInterface.java

package observer.one.subject;import observer.one.observers.ObserverInterface;public interface SubjectInterface {    public void registerObserver(ObserverInterface o);    public void removeObserver(ObserverInterface o);    public void notifyObservers();}

具体主题:WeatherData.java

package observer.one.subject.impl;import java.util.ArrayList;import observer.one.observers.ObserverInterface;import observer.one.subject.SubjectInterface;public class WeatherData implements SubjectInterface{    private ArrayList<ObserverInterface> observers;    //温度    private String temperature;    //湿度    private String humidity;    //气压    private String pressure;    public WeatherData() {        observers=new ArrayList<ObserverInterface>();    }    /**     * 订阅     */    public void registerObserver(ObserverInterface o) {        observers.add(o);    }    /**     * 取消订阅     */    public void removeObserver(ObserverInterface o) {        if(observers.indexOf(o)>=0){            observers.remove(o);        }    }    /**     * 通知观察者     */    public void notifyObservers() {        for(ObserverInterface o:observers){            o.update(temperature, humidity, pressure);        }    }    public void setDataChange() {        notifyObservers();    }    /**     * 数据改变后,通知观察者     * @param temperature     * @param humidity     * @param pressure     */    public void setNewData(String temperature,String humidity,String pressure){        this.temperature=temperature;        this.humidity=humidity;        this.pressure=pressure;        setDataChange();    }}

抽象观察者:ObserverInterface.java

package observer.one.observers;public interface ObserverInterface {    void update(String temperature,String humidity,String pressure);}

实际上,我们看到这里就会发现有点问题,一旦我们的数据参数发生变化,就需要去修改抽象观察者方法,而具体观察者也需要去修改相关方法的实现,耦合性较大。

具体观察者:Observer1.java

package observer.one.observers.impl;import java.util.Date;import observer.one.observers.ObserverInterface;import observer.one.subject.SubjectInterface;public class Observer1 implements ObserverInterface{    private SubjectInterface subject;    //温度    private String temperature;    //湿度    private String humidity;    //气压    private String pressure;    public Observer1(SubjectInterface subject) {        this.subject = subject;        subject.registerObserver(this);    }    public void register(){        System.out.println("------观察者1订阅成功-------");        subject.registerObserver(this);    }    public void cancelRegister(){        System.out.println("------观察者1取消订阅了-------");        subject.removeObserver(this);    }    public void update(String temperature, String humidity, String pressure) {        this.humidity=humidity;        this.pressure=pressure;        this.temperature=temperature;        display();    }    private void display() {        System.out.println("观察者1----数据更新了("+new Date()+")-温度:"+temperature+"-湿度:"+humidity+"-气压:"+pressure);    }}

测试方法:

这里为了方便演示,我写了两个具体观察者(Observer1 ,Observer2 ),实际上在具体实现中只需要写一个,实例化多个即可。

package observer.one;import observer.one.observers.impl.Observer1;import observer.one.observers.impl.Observer2;import observer.one.subject.impl.WeatherData;public class Test {    public static void main(String[] args) {        WeatherData weatherData=new WeatherData();        //订阅天气日报        Observer1 observer1=new Observer1(weatherData);        //订阅天气日报        Observer2 observer2=new Observer2(weatherData);        weatherData.setNewData("10", "20", "30");        observer1.cancelRegister();        weatherData.setNewData("10.5", "14.23", "15.65");        observer1.register();        weatherData.setNewData("15.5", "18.23", "17");    }}

输出结果:

方式二:观察者主动拉取数据

这里我们采取JDK自己提供的抽象主题与抽象观察者的方式来实现。

抽象主题:java.util.Observable

抽象观察者:java.util.Observer

具体主题:WeatherData.java

package observer.two.subject;import java.util.Observable;public class WeatherData extends Observable{    //温度    private String temperature;    //湿度    private String humidity;    //气压    private String pressure;    /**     * 数据更新方法     * @param temperature     * @param humidity     * @param pressure     */    public void setNewData(String temperature,String humidity,String pressure){        this.temperature=temperature;        this.humidity=humidity;        this.pressure=pressure;        setChanged();        notifyObservers("数据更新了,快来获取吧");    }    public String getTemperature() {        return temperature;    }    public String getHumidity() {        return humidity;    }    public String getPressure() {        return pressure;    }}

由上面代码可以看出,具体主题主动暴露出属性的getter方法,当数据更新时调用setChange()方法,通知观察者此时有数据更新,你可以来获取了。

具体观察者:Observer1.java

package observer.two.observers;import java.util.Date;import java.util.Observable;import java.util.Observer;import observer.two.subject.WeatherData;public class Observer1 implements Observer{    private Observable observable;    //温度    private String temperature;    //湿度    private String humidity;    //气压    private String pressure;    public Observer1(Observable observable) {        super();        this.observable = observable;        System.out.println("------观察者1订阅了-------");        observable.addObserver(this);    }    public void cancelRegister(){        System.out.println("------观察者1取消订阅了-------");        observable.deleteObserver(this);    }    /**     * 数据更新通知     */    public void update(Observable o, Object arg) {        System.out.println("气象台说:"+arg+"");        System.out.println("观察者1:嗯,这就去");        if(o instanceof WeatherData){            WeatherData data=(WeatherData)o;            this.humidity=data.getHumidity();            this.pressure=data.getPressure();            this.temperature=data.getTemperature();        }        display();        System.out.println("数据提取完毕,并已展示");    }    /**     * 数据打印     */    private void display() {        System.out.println("观察者1----数据更新了("+new Date()+")-温度:"+temperature+"-湿度:"+humidity+"-气压:"+pressure);    }}

此时,具体观察者调用update方法,主动拉取主题的最新数据,并显示出来。

测试方法:

package observer.two;import observer.two.observers.Observer1;import observer.two.subject.WeatherData;public class Test {    public static void main(String[] args) {        WeatherData weatherData=new WeatherData();        /*观察者1订阅天气日报*/        Observer1 observer1=new Observer1(weatherData);        Observer1 observer2=new Observer1(weatherData);        weatherData.setNewData("10", "79", "18");    }}

输出结果:

两种实现方式对比

细心的同学应该可以发现,自己编写抽象主题代码时,我们编写的接口(interface),而JDK官方提供的观察者模式中,抽象主题采用的是继承的形式实现。归根结底这两种实现方式的对比,是主题推送数据和观察者拉取数据的对比,以及实现接口和继承父类的对比。

推送数据和拉取数据的对比:

推送数据的实现方式主题需要把所有的参数都推送给观察者们,这里就需要事先在update方法中指定所有的参数,一旦参数发生改变,不仅要改变主题的方法,还需要改变观察者接口方法。
而拉取数据的实现方式只需要改变主题参数,不需要改变观察者接口方法,观察者们依旧根据自己的需要去获取数据。

实现接口和继承父类的对比:

由于JAVA只支持单继承,实现接口和继承父类的优劣势大家也应该都很清楚。

对比结果:如果可以的话,我们应该自己去实现自己的抽象主题,而不采用JDK官方提供的实现方式,毕竟继承的形式有一定的弊端。在数据获取方面,最好采用具体观察者拉取的形式,这样更有利于后期进行扩展。

相关文章:《设计模式——策略模式:会员价格体系的简单实现》
案例转载于https://my.oschina.net/visionit/blog/758673

0 0