学习:设计模式之观察者模式

来源:互联网 发布:2017年网络诈骗案 编辑:程序博客网 时间:2024/05/22 16:53

本文来自www.lanttor.org

Internet气象站

经验

有一个模式可以帮助你的对象知悉现况,不会错过对象感兴趣的事。对象甚至在运行时可决定是否要继续被通知。观察者模式是JDK中使用最多的模式之一。非常有用。

气象监测应用的概况

g1

认识观察者模式

报纸和杂志的订阅是怎么回事:

  • 报社的业务是出版报纸;
  • 向某家报社订阅报纸,只要你是他们的用户,你就会收到新报纸;
  • 你不想看报纸的话,取消订阅,他们就不会再送新报纸来;
  • 只要报社运营,就会一直有人(或单位)向他们订阅报纸或取消订阅。

 

出版者 + 订阅者 = 观察者模式

出版者改称为“主题”(subject);订阅者改称为“观察者”(Observer)。

g2

 

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

定义观察者模式

实现观察者模式的方法不只一种,但是以包含Subject和Observer接口的类设计的做法最常见。

g3

设计原则

为了交互对象之间的松耦合设计而努力。当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降低到了最低。

设计气象站

g4

气象站代码

Subject.java代码:

package headfirst.observer.weather;

 

public interface Subject {

    public void registerObserver(Observer o);

    public void removeObserver(Observer o);

    public void notifyObservers();

}

 

 

Observer.java代码:

package headfirst.observer.weather;

 

public interface Observer {

    public void update(float temp, float humidity, float pressure);

}

 

 

DisplayElement.java代码:

package headfirst.observer.weather;

 

public interface DisplayElement {

    public void display();

}

 

WeatherData.java代码:

package headfirst.observer.weather;

 

import java.util.*;

 

public class WeatherData implements Subject {

    private ArrayList observers;

    private float temperature;

    private float humidity;

    private float pressure;

   

    public WeatherData() {

        observers = new ArrayList();

    }

   

    public void registerObserver(Observer o) {

        observers.add(o);

    }

   

    public void removeObserver(Observer o) {

        int i = observers.indexOf(o);

        if (i >= 0) {

            observers.remove(i);

        }

    }

   

    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 float getTemperature() {

        return temperature;

    }

   

    public float getHumidity() {

        return humidity;

    }

   

    public float getPressure() {

        return pressure;

    }

}

 

 

CurrentConditionsDisplay.java代码:

package headfirst.observer.weather;

   

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;

    private float humidity;

    private Subject weatherData;

   

    public CurrentConditionsDisplay(Subject weatherData) {

        this.weatherData = weatherData;

        weatherData.registerObserver(this);

    }

   

    public void update(float temperature, float humidity, float pressure) {

        this.temperature = temperature;

        this.humidity = humidity;

        display();

    }

   

    public void display() {

        System.out.println("Current conditions: " + temperature

            + "F degrees and " + humidity + "% humidity");

    }

}

 

 

ForecastDisplay.java代码

package headfirst.observer.weather;

 

import java.util.*;

 

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);

    }

 

    public void update(float temp, float humidity, float pressure) {

                lastPressure = currentPressure;

        currentPressure = pressure;

 

        display();

    }

 

    public void display() {

        System.out.print("Forecast: ");

        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");

        }

    }

}

 

 

StaticsDisplay.java代码:

package headfirst.observer.weather;

 

import java.util.*;

 

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);

    }

 

    public void update(float temp, float humidity, float pressure) {

        tempSum += temp;

        numReadings++;

 

        if (temp > maxTemp) {

            maxTemp = temp;

        }

 

        if (temp < minTemp) {

            minTemp = temp;

        }

 

        display();

    }

 

    public void display() {

        System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)

            + "/" + maxTemp + "/" + minTemp);

    }

}

 

HeatIndexDisplay.java代码:

package headfirst.observer.weather;

 

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);

    }

}

 

测试结果

测试代码(WeatherStationHeatIndex.java):

package headfirst.observer.weather;

 

import java.util.*;

 

public class WeatherStationHeatIndex {

 

    public static void main(String[] args) {

        WeatherData weatherData = new WeatherData();

        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(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);

    }

}

 

 

结果:

Current conditions: 80.0F degrees and 65.0% humidity

Avg/Max/Min temperature = 80.0/80.0/80.0

Forecast: Improving weather on the way!

Heat index is 82.95535

Current conditions: 82.0F degrees and 70.0% humidity

Avg/Max/Min temperature = 81.0/82.0/80.0

Forecast: Watch out for cooler, rainy weather

Heat index is 86.90124

Current conditions: 78.0F degrees and 90.0% humidity

Avg/Max/Min temperature = 80.0/82.0/78.0

Forecast: More of the same

Heat index is 83.64967

 

使用java内置的观察者模式

java.util包内包含最基本的Observer接口和Observable类。

g5

导入Observer/Observable:

import java.util.Observable;

import java.util.Observer;

 

使用Observable类代替之前的Subject接口;Observable是类,所以WeatherData继承Observable。

 

Observable如何送出通知:

先调用setChanged()方法,标记状态已经改变的事实;

然后调用notifyObservers()/notifyObservers(Object arg)

 

Observer如何接收通知:

观察者实现了update()方法,只是方法的签名更改为update(Observable o, Object arg)

在update()中,先确定观察者属于WeatherData类型,然后利用getter方法获取相应的更新数据,然后调用display()。

利用内置的观察者模式重做气象站

WeatherData.java代码:

package headfirst.observer.weatherobservable;

import java.util.Observable;

import java.util.Observer;

public class WeatherData extends Observable {

    private float temperature;

    private float humidity;

    private float pressure;

    public WeatherData() { }

    public void measurementsChanged() {

        setChanged();

        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;

    }

}

 

 

CurrentConditionsDisplay.java代码:

package headfirst.observer.weatherobservable;

import java.util.Observable;

import java.util.Observer;

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    Observable observable;

    private float temperature;

    private float humidity;

    public CurrentConditionsDisplay(Observable observable) {

        this.observable = observable;

        observable.addObserver(this);

    }

    public void update(Observable obs, Object arg) {

        if (obs instanceof WeatherData) {

            WeatherData weatherData = (WeatherData)obs;

            this.temperature = weatherData.getTemperature();

            this.humidity = weatherData.getHumidity();

            display();

        }

    }

    public void display() {

        System.out.println("Current conditions: " + temperature

            + "F degrees and " + humidity + "% humidity");

    }

}

 

JDK Swing中观察者模式的使用

一个简单的Swing API:JButton。如果你观察一下JButton的超类AbstractButton,会看到许多增加与删除Listener的方法。这些方法可以让观察者感应到Swing组件的不同类型事件。比方说,ActionListener让你“倾听”可能发生在按钮上的动作,例如按下按钮。这些方法就是典型的观察者模式的使用。

g6

总结

  • 观察者模式定义了对象之间一对多的关系。
  • 主题(也就是可观察者)用一个共同的接口来更新观察者。
  • 观察者和可观察者之间用松耦合方式结合。可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
  • 使用此模式时,你可从被观察者处推(push)或拉(pull)数据。(推的方式被认为更正确)。
  • 有多个观察者时,不可以依赖特定的通知次序。
  • Java有多种观察者模式的实现,包括了通用的java.util.Observable和java.util.Observer。
  • 要注意java.util.Observable实现上所带来的问题。
  • 如果有必要的话,可以实现自己的Observable,这并不困难。
  • Swing大量使用观察者模式,许多GUI框架也是如此。
  • 此模式也被应用在许多地方,例如:JavaBeans,RMI
0 0