本文来自www.lanttor.org
经验 有一个模式可以帮助你的对象知悉现况,不会错过对象感兴趣的事。对象甚至在运行时可决定是否要继续被通知。观察者模式是JDK中使用最多的模式之一。非常有用。 |
报纸和杂志的订阅是怎么回事:
出版者 + 订阅者 = 观察者模式
出版者改称为“主题”(subject);订阅者改称为“观察者”(Observer)。
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变时,它的所有依赖者都会收到通知并自动更新。 |
实现观察者模式的方法不只一种,但是以包含Subject和Observer接口的类设计的做法最常见。
设计原则 为了交互对象之间的松耦合设计而努力。当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。 观察者模式提供了一种对象设计,让主题和观察者之间松耦合。 松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降低到了最低。 |
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.util包内包含最基本的Observer接口和Observable类。
导入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"); } }
|
一个简单的Swing API:JButton。如果你观察一下JButton的超类AbstractButton,会看到许多增加与删除Listener的方法。这些方法可以让观察者感应到Swing组件的不同类型事件。比方说,ActionListener让你“倾听”可能发生在按钮上的动作,例如按下按钮。这些方法就是典型的观察者模式的使用。