设计模式——观察者模式

上一篇说了策略模式,这一篇,看看观察者模式。

还是先讲述一下这篇文章代码示例的背景,需求是一个气象站,会实时观察大气数据,每次更新数据,会有多个布告板,及时显示更新的数据。

刚拿过来,一般思路可能是这样

public class WeatherData{
//声明变量

public void measurementsChanged(){
float temp=getTemperature();
...

currentConditionsDisplay.update(temp,..);
statisticsDisplay.update(temp,...)
}

}

这显然有悖上一篇博客的设计原则:1.针对实现编程,会导致日后修改布告板的时候要大量修改代码,2.会改变的部分没有封装起来,日后需求改变的时候,将进行代码的大量替换。

既然是说观察者模式,这次就直奔主题。

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

实现观察者模式的方式不止一种,但是以包含Subject与Observer接口的类设计最为常见:下面是上面需求的类图



下面看详细代码:

主题接口:

public interface Subject {
    //用于注册观察者
    public void registerObserver(Observer o);
    //删除观察者
    public void removeObserver(Observer o);
    //当状态发生改变时,调用此方法,使得所有观察者得到通知
    public void notifyObservers();
}

观察者接口:

public interface Observer {
    public void update(float temp,float humidity,float pressure);
}

更新行为接口(稍后可以明白为啥有这个接口):

public interface DisplayElement {
    public void display();
}
 
 

具体主题类:

public class WeatherData implements Subject {
    //定义数组,用来记录所有观察者
    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){
        observers=new ArrayList();
    }

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

    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i>=0){
            observers.remove(i);
        }
    }

    //遍历所有的观察者,调用他们的update方法,也就是告知他们新的数据
    @Override
    public void notifyObservers() {
        for(int i=0;i<observers.size();i++){
            Observer observer= (Observer) observers.get(i);
            observer.update(temperature,humidity,pressure);
        }
    }

    public void measurementsChanged(){
        notifyObservers();
    }

    //用来模拟改变数据
    public void setMeasurements(float temperature,float humidity,float pressure)
    {
        this.temperature=temperature;
        this.humidity=humidity;
        this.pressure=pressure;
        measurementsChanged();
    }

某个布告板的实现类:

public class CurrentConditionsDisplay implements Observer,DisplayElement {
    private float temperature;
    private float humidity;
    private  Subject weatherData;


    //在构造器中指定Subject具体类,并注册为一个观察者
    public  CurrentConditionsDisplay(Subject weatherData){
        this.weatherData=weatherData;
        weatherData.registerObserver(this);
    }

    //观察者的update方法将在被观察者的类中被动调用
    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature=temp;
        this.humidity=humidity;
        display();
    }

    //display方法模拟将数据显示出来
    @Override
    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 currentConditionsDisplay=new CurrentConditionsDisplay(weatherData);
    weatherData.setMeasurements(80,65,30.3f);
    weatherData.setMeasurements(82,63,40.1f);

}

结果:

Current conditions:80.0F degrees and 65.0humidity

Current conditions:82.0F degrees and 63.0humidity

程序实现了我们的需求,当数据被新Set时,CurrentConditions布告板会及时更新显示的数据;

下面看看程序的亮点:

    我们设计的程序对象之间是松耦合的,所谓松耦合,我的理解就是在一个程序中,各种类实例对象之间不要有太大的依赖关系,比如我们这次程序,当我们有一个新的类要注册为观察者的时候,主题类不需要为了兼容新的类而修改本身的的代码,甚至不需要关心是谁在要求,也不管会把通知传给那个具体类,想要注册为观察者的对象,只要实现Observer接口,并调用注册方法,主题类就会发送通知给所有实现了Observer接口的对象。

    松耦合的设计让我们的程序更有弹性,在应对变化时,将对象之间的依赖降到最低,也就将需要被更改的代码降到最低。

这也是我们的另一个设计原则:为了交互对象之间的松耦合设计而努力。

在java中,有内置的观察者模式供我们使用。与上面的项目例子类似,区别在于

1.“主题”类扩展自ava.util.Observable类,而观察者要实现java.util.Observer 接口。例子中的registerObserver 和

removeObserver()方法改为了addObserver 和 deleteObserver。

2.在数据传递时,在我们例子的数据传递方法基础上重载了一个带参方法 notofyObservers(Object arg)update(Observable o, object arg)

当传递通知时,用带参的方法,可以将任何数据对象传递给每一个观察者。

3.新添加了一个setChanged()方法,protected synchronized void setChanged() { changed = true;}


这个方法只是改变了全局变量changed的标识,changed变量存在的意义在于,每次通知观察者时,会查看changed变量是否为true,如果不是,则不发送通知。而setchanged方法的意义就在于可以自主的过滤在某些时候,不去发送通知。比如我们上个例子中,真是的情况是一个传感器接受大气数据,而不是我们set新的数据。当大气数据变化量特别小的时候,我们希望忽略不计,以提高性能。所以我们可以做一些判定,当变化的数值高于我们的最低要求是,调用setChanged。这样发送通知才可以成功。当然,发送同志之后,要将此标识设为false。

内置观察者模式的缺陷

1.上面提到了,主题类扩展自一个Observable类,而不是一个接口。java并不能多继承,而且这有悖我们的一个设计原则,导致代码可复用性极差。

2.setChanged方法是protected修饰的,这意味着除非你继承Observable,否则你无法创建新的Observable实例,组合到你自己的对象中来这又违背了我们的一个设计原则。

所以只要理解了观察者模式的思想,可以像上面例子一样,自己实现一套观察者模式,用于使用。

猜你喜欢

转载自blog.csdn.net/wy9717/article/details/80117361