设计模式学习--观察者模式(Oberser Pattern)

设计模式学习–观察者模式(Oberser Pattern)

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

怎么设计一个观察者模式的程序?
确定两个对象:1. 主题
2.观察者

确定这两个对象之间的关系:
主题对象管理某些数据,一旦数据发生改变,会主动向观察者进行通知,然而观察者不必向主题进行索取。
主题并不知道具体的观察者是谁,这是它们之间的关系。

以上涉及到的设计原则:
为了交互对象之间的松耦合设计而努力

具体实例:气象站的实现

  1. 定义一个主题接口Subject
[java] view plaincopy
package observerPattern;  
/** 
 * 主题接口 
 */  
public interface Subject {  
    public void registerObserver(Observer o);   //这两个方法都需要一个观察者作为变量,该观察者是用那个来注册和删除的  
    public void removeObserver(Observer o);  
    public void notifyObserver();               //当主题状态发生改变时,这个方法会被调用,以通知所有的观察者  
}  

2. 定义一个观察者接口Observer

[java] view plaincopy
package observerPattern;  
/** 
 * 观察者接口 
 */  
public interface Observer {  
    public void update(float temp, float humidity, float pressure);  
}  

3. 定义一般气象布告板接口DisplayElement

[java] view plaincopy
package observerPattern;  

/** 
 * 公告板接口 
 */  
public interface DisplayElement {  
    public void display();  
}     

4. 定义主题类:WeatherData实现接口

[java] view plaincopy
package observerPattern;  

import java.util.ArrayList;  

/** 
 * WeatherData实现了Subject接口 
 */  
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);  
        }  
    }  

    @Override  
    public void notifyObserver() {  
        for(int i = 0; i < observers.size(); i++) {  
            Observer observer = (Observer)observers.get(i);  
            observer.update(temperature, humidity, pressure);  
        }  
    }  

    public void measurementsChanged() {  
        notifyObserver();  
    }  

    public void setMeasurements(float temperature, float humidity, float pressure) {  
        this.temperature = temperature;  
        this.humidity = humidity;  
        this.pressure = pressure;  
        measurementsChanged();  
    }  

}  

5. 定义四个布告板类实现观察者接口和布告板接口

[java] view plaincopy
package observerPattern;  
/** 
 * 观察者类实现观察者接口和显示板接口 
 */  
public class CurrentConditionDisplay implements Observer, DisplayElement {  
    private float temperature;  
    private float humidity;  
    private Subject weathderData;  


    public CurrentConditionDisplay(Subject weathderData) {  
        this.weathderData = weathderData;  
        weathderData.registerObserver(this);        //注册  
    }  

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

    @Override  
    public void update(float temp, float humidity, float pressure) {  
        this.temperature = temp;  
        this.humidity = humidity;  
        display();  
    }  

}  

[java] view plaincopy
package observerPattern;  
/** 
 * 天气统计布告板 
 */  
public class StatisticsDisplay implements Observer, DisplayElement {  
    private float maxTemp = 0.0f;;  //最大温度  
    private float minTemp = 200;    //最小温度  
    private float tempSum = 0.0f;   //统计温度和  
    private int numReadings;        //统计温度次数  
    private WeatherData weatherData;  


    public StatisticsDisplay(WeatherData weatherData) {  
        this.weatherData = weatherData;  
        weatherData.registerObserver(this);  
    }  

    @Override  
    public void display() {  
        System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);  
    }  

    @Override  
    public void update(float temp, float humidity, float pressure) {  
        tempSum += temp;  
        numReadings++;  

        if(temp > maxTemp) {  
            maxTemp = temp;  
        }  
        if(temp < minTemp) {  
            minTemp = temp;  
        }  

        display();  
    }  

}  

[java] view plaincopy
package observerPattern;  
/** 
 * 天气预报布告板 
 */  
public class ForecastDisplay implements Observer, DisplayElement {  
    private float currentPressure = 29.92f; //当前气压  
    private float lastPressure;             //以往气压  
    private WeatherData weatherData;  

    public ForecastDisplay(WeatherData weatherData) {  
        this.weatherData = weatherData;  
        weatherData.registerObserver(this);  
    }  

    @Override  
    public void display() {  
        System.out.println("Forcast:");  
        if(currentPressure > lastPressure) {  
            System.out.println("Improving weather on the way!");  
        } else if(currentPressure == lastPressure) {  
            System.out.println("more of the same");  
        } else if(currentPressure < lastPressure) {  
            System.out.println("Watch out for cooler, rainy weather");  
        }  
    }  

    @Override  
    public void update(float temp, float humidity, float pressure) {  
        lastPressure = currentPressure;  
        currentPressure = pressure;  
        display();  
    }  

}  

[java] view plaincopy
package observerPattern;  

/** 
 * 酷热指数布告板 
 * 注:那个计算酷热指数的公式不必深究 
 */  
public class HeatIndexDisplay implements Observer, DisplayElement {  
    float heatIndex = 0.0f;  
    private WeatherData weatherData;  

    public HeatIndexDisplay(WeatherData weatherData) {  
        this.weatherData = weatherData;  
        weatherData.registerObserver(this);  
    }  

    public void update(float t, float rh, float pressure) {  
        heatIndex = computeHeatIndex(t, rh);  
        display();  
    }  

    private float computeHeatIndex(float t, float rh) {  
        float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh)   
            + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh))   
            + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +  
            (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 *   
            (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) +   
            (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +  
            0.000000000843296 * (t * t * rh * rh * rh)) -  
            (0.0000000000481975 * (t * t * t * rh * rh * rh)));  
        return index;  
    }  

    public void display() {  
        System.out.println("Heat index is " + heatIndex);  
    }  
}  

6. 来吧,开始测试

[java] view plaincopy
package observerPattern;  

/** 
 * 测试类
 * 
 */  
public class WeatherStation {  
    public static void main(String[] args) {  
        //建立一个WeatherData对象  
        WeatherData weatherData = new WeatherData();  

        //第一个布告板  
        CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(  
                weatherData);  
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);  
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);  
        HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);  

        // 模拟新的气象数据  
        weatherData.setMeasurements(80, 65, 30.4f);  
        weatherData.setMeasurements(82, 70, 29.2f);  
        weatherData.setMeasurements(78, 90, 29.2f);  
    }  
}  

7. 测试结果:
[plain] view plaincopy
Current coditions: 80.0F degress and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forcast:
Improving weather on the way!
Heat index is 82.95535
Current coditions: 82.0F degress and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forcast:
Watch out for cooler, rainy weather
Heat index is 86.90124
Current coditions: 78.0F degress and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forcast:
more of the same
Heat index is 83.64967

以上的观察者模式实现是通过主题以“推”的方式通知观察者们,观察者可以在一次通知中一口气得到所有东西。

因为观察者与主题发生了争吵,观察者有自己的想法,希望能“拉”走主题的状态,然而Java内置的Observer模式就支持这样,下面来看看吧。

1. 继承Observable类的WeatherData(不再需要自定义接口了,但这样真的好吗?)
[java] view plaincopy
package weatherObservable;

import java.util.Observable;
/**
* 使用Java内置的观察者模式
*/
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;

/** 
 * 我们的构造器不再需要为了记住观察者们而建立数据结构了 
 */  
public WeatherData(){}  

public void measurementsChanged() {  
    setChanged();       //Observable类方法  
    notifyObservers();  
}  

public void setMeasurements(float temperature, float humidity, float pressure) {  
    this.temperature = temperature;  
    this.humidity = humidity;  
    this.pressure = pressure;  
    measurementsChanged();  
}  

public float getTemperature() {  
    return temperature;  
}  

public float getHumidity() {  
    return humidity;  
}  

public float getPressure() {  
    return pressure;  
}  

}

2. 4个布告板中的代码稍微发生了点变化

[java] view plaincopy
package weatherObservable;  

import java.util.Observable;  
import java.util.Observer;  


/** 
 * 实现Java内置的观察者接口,布告板不变 
 */  
public class CurrentConditionDisplay implements Observer, DisplayElement{  
    Observable observable;  
    private float temperature;  
    private float humidity;  

    public CurrentConditionDisplay(Observable observable) {  
        this.observable = observable;  
        observable.addObserver(this);       //登记为观察者  
    }  

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


    /** 
     * 在这个方法当中,先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和温度测量值,最后调用display(); 
     */  
    @Override  
    public void update(Observable obs, Object arg) {  
        if(obs instanceof WeatherData) {  
            WeatherData weatherData = (WeatherData) obs;  
            this.temperature = weatherData.getTemperature();  
            this.humidity = weatherData.getHumidity();  
            display();  
        }  
    }  

}  

[java] view plaincopy
package weatherObservable;  

import java.util.Observable;  
import java.util.Observer;  

/** 
 * 天气预报布告板 
 */  
public class ForecastDisplay implements Observer, DisplayElement {  
    private Observable observable;  
    private float currentPressure = 29.92f; //当前气压  
    private float lastPressure;             //以往气压  

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

    @Override  
    public void display() {  
        System.out.println("Forcast:");  
        if(currentPressure > lastPressure) {  
            System.out.println("Improving weather on the way!");  
        } else if(currentPressure == lastPressure) {  
            System.out.println("more of the same");  
        } else if(currentPressure < lastPressure) {  
            System.out.println("Watch out for cooler, rainy weather");  
        }  
    }  


    @Override  
    public void update(Observable o, Object arg) {  
        if (o instanceof WeatherData) {  
            WeatherData weatherData = (WeatherData)observable;  
            lastPressure = currentPressure;  
            currentPressure = weatherData.getPressure();  
            display();  
        }  
    }  

}  

[java] view plaincopy
package weatherObservable;  

import java.util.Observable;  
import java.util.Observer;  

/** 
 * 天气统计布告板 
 */  
public class StatisticsDisplay implements Observer, DisplayElement {  
    private float maxTemp = 0.0f;;  //最大温度  
    private float minTemp = 200;    //最小温度  
    private float tempSum = 0.0f;   //统计温度和  
    private int numReadings;        //统计温度次数  
    private Observable observable;  

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

    @Override  
    public void display() {  
        System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);  
    }  

    @Override  
    public void update(Observable obs, Object arg) {  
        if(obs instanceof WeatherData) {  
            WeatherData weatherData = (WeatherData)obs;  
            float temp = weatherData.getTemperature();  
            tempSum += temp;  
            numReadings++;  

            if (temp > maxTemp) {  
                maxTemp = temp;  
            }  

            if (temp < minTemp) {  
                minTemp = temp;  
            }  

            display();  
        }  
    }  


}  

[java] view plaincopy
package weatherObservable;  

import java.util.Observable;  
import java.util.Observer;  

/** 
 * 酷热指数布告板 
 * 注:那个计算酷热指数的公式不必深究 
 */  
public class HeatIndexDisplay implements Observer, DisplayElement {  
    float heatIndex = 0.0f;  
    private WeatherData weatherData;  
    private Observable observable;  

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

    private float computeHeatIndex(float t, float rh) {  
        float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh)   
            + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh))   
            + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +  
            (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 *   
            (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) +   
            (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +  
            0.000000000843296 * (t * t * rh * rh * rh)) -  
            (0.0000000000481975 * (t * t * t * rh * rh * rh)));  
        return index;  
    }  

    public void display() {  
        System.out.println("Heat index is " + heatIndex);  
    }  

    @Override  
    public void update(Observable obs, Object arg) {  
        if(obs instanceof WeatherData) {  
            WeatherData weatherData = (WeatherData)observable;  
            float t = weatherData.getTemperature();  
            float rh = weatherData.getHumidity();  
            heatIndex = computeHeatIndex(t, rh);  
        }  
        display();  
    }  
}  

3. 测试类不变

[java] view plaincopy
package weatherObservable;  

/** 
 * 测试类 
 */  
public class WeatherStation {  
    public static void main(String[] args) {  
        //建立一个WeatherData对象  
        WeatherData weatherData = new WeatherData();  

        //第一个布告板  
        CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(  
                weatherData);  
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);  
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);  
        HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);  

        // 模拟新的气象数据  
        weatherData.setMeasurements(80, 65, 30.4f);  
        weatherData.setMeasurements(82, 70, 29.2f);  
        weatherData.setMeasurements(78, 90, 29.2f);  
    }  
}  

4. 但测试结果发生了变化:观察者被通知的次序发生了变化
[plain] view plaincopy
Heat index is 82.95535
Forcast:
Improving weather on the way!
Avg/Max/Min temperature = 80.0/80.0/80.0
Current conditions: 80.0F degrees and 65.0% humidity
Heat index is 86.90124
Forcast:
Watch out for cooler, rainy weather
Avg/Max/Min temperature = 81.0/82.0/80.0
Current conditions: 82.0F degrees and 70.0% humidity
Heat index is 83.64967
Forcast:
more of the same
Avg/Max/Min temperature = 80.0/82.0/78.0
Current conditions: 78.0F degrees and 90.0% humidity

以上的实现被认为不是那么“正确”的,为什么呢?
有以下原因:
1. Observable是一个“类”,而不是一个接口,也没有实现一个接口,限制了它的使用和复用。
2. Observable将关键的方法保护起来了,违反了 “多用组合,少用继承”的设计原则。

当然在JDK中不只有这个地方用到了观察者模式,比如以下几个地方也用到了:
1. Swing
2. JavaBeans
3. RMI

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值