观察者模式

有这样一个需求:天气预报功能:有一个WeatherData 对象提供数据,需要做3个面板显示不同情况的数据:当前天气;天气统计;天气预报。在数据更新时保持3个面板的数据也要同时更新。再有可以允许合作者自制面板并且接数据,并且保持数据实时更新。
 
给出的WeatherData 对象大概是这种:
public class WeatherData {
	public double getTemperature(){}
	public double getHumidity(){}
	public double getPressure(){}
	public void measurementsChanged(){
		//代码加在这里
	}
}

  

开始进行修改,那么最low的写法是这种:
public void measurementsChanged(){
	double temperature = getTemperature();
	double humidity = getHumidity();
	double pressure = getPressure();
	
	currentConditionsDisplay.update(temperature,humidity,pressure);
	statisticsDisplay.update(temperature,humidity,pressure);
	forecastDisplay.update(temperature,humidity,pressure);
}

  

一旦需求有变,需要反复修改代码,每加一个面板或少一个面板,代码都需要修改。
所以要把变化的部分封装起来。
 
观察者模式:定义了对象之间一对多的依赖关系。这样一来,一个对象(主题)改变状态时,他的所有依赖者(观察者)都会收到通知并自动更新。
 
 
主题依赖的是实现Observer接口的对象列表,所以我们可以随时增加和删除观察者。
主题不用管是哪个具体的类实现了Observer接口,只要把信息发到给实现接口的对象即可,进而不需要修改主题内的代码。
 
依照图示,重新编写代码:
public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObserver();
}
public interface Observer {
    public void update(double temperature,double humidity,double pressure);
}
public class WeatherData implements Subject{
    private ArrayList<Observer> observers;
    private double temperature;
    private double humidity;
    private double pressure;
    
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    public void removeObserver(Observer observer) {
        int i = observers.indexOf(observer);
        if(i >= 0){
            observers.remove(i);
        }
    }
    public void notifyObserver() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = observers.get(i);
            observer.update(temperature, humidity, pressure);
        }
    }
    public void measurementsChanged(){
        notifyObserver();
    }
    public void setMeasurements(double temperature,double humidity,double pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
public class CurrentConditionDisplay implements DisplayElement, Observer {
    private double temperature;
    private double humidity;
    private double pressure;
    private Subject subject;
    public CurrentConditionDisplay(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }
    public void update(double temperature, double humidity, double pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
    public void display() {
        System.out.println("CurrentCondition:..."+temperature+"--"+humidity+"--"+pressure);
    }
}
剩下两个面板类似,不再贴出
现在测试一下:
public class Test {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionDisplay ccd = new CurrentConditionDisplay(weatherData);
        StatisticsDisplay sd = new StatisticsDisplay(weatherData);
        ForecastDisplay fd = new ForecastDisplay(weatherData);
        weatherData.setMeasurements(12, 15, 55);
    }
}
问题:用主题对象去主动推送信息,好处是可以避免观察者多次申请获取数据。
但是缺点就是可能推送了一些观察者不需求的数据,浪费了资源。
 
所以主题对象也可以提供公开的getter方法来获取数据。
 
JDK自己拥有观察者模式
util包下包含了 Observer接口 和 Observable类
 
Observable中多出了个属性,boolean changed;用来记录信息是否改变。
所以当推送消息等方法时,先判断changed是否为true,只有在改变为真的状态时,才回推送消息。
同样的,当需要修改状态时,也有对应的 setChanged() 和 clearChanged(),具体使用可以看源码。
 
这样做更加灵活。比如在要推送的信息改变幅度非常小时,不想进行推送,那么就可以在一个范围判断后,再执行 setChanged()
 
用JDK的观察者模式再来实现一次上述功能。
 
 1 public class WeatherData extends Observable{
 2     private double temperature;
 3     private double humidity;
 4     private double pressure;
 5     
 6     public WeatherData(){}
 7     public void measurementsChanged(){
 8         setChanged();
 9         notifyObservers();
10     }
11     public void setMeasurements(double temperature,double humidity,double pressure){
12         this.temperature = temperature;
13         this.humidity = humidity;
14         this.pressure = pressure;
15         measurementsChanged();
16     }
17     public double getTemperature() {
18         return temperature;
19     }
20     public double getHumidity() {
21         return humidity;
22     }
23     public double getPressure() {
24         return pressure;
25     }
26 }
1、成员变量不再需要单独的集合,同时注册、删除等方法不再使用都是因为已经继承了Observable的方法。
2、在信息改变的时候记得加上setChanged() 方法修改状态。
3,加了getter方法,可以方便观察者有目的性的取值。
 
再次重写面板代码:
public class CurrentConditionDisplay implements DisplayElement, Observer {
    private double temperature;
    private double humidity;
    private double pressure;
    private Observable observable;
    public CurrentConditionDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData){
            WeatherData wd = (WeatherData) o;
            this.temperature = wd.getTemperature();
            this.humidity = wd.getHumidity();
            this.pressure = wd.getPressure();
            display();
        }
    }
    public void display() {
        System.out.println("CurrentCondition:..."+temperature+"--"+humidity+"--"+pressure);
    }
}
 
使用jdk自带的观察者模式虽然很方便,但是由于可观察者是以类的形式而非接口的形式存在,自然会有不方便之处。所以有必要的话,可以实现自己写的Observable。
 
 
以上学习参考《Head First 设计模式》
 
posted @ 2017-11-09 13:31  BlueK  阅读(219)  评论(0编辑  收藏  举报