除了我们可以自己定义观察者之外,我们还可以使用java工具类中的API实现,但比起我们自己实现的接口,它会存在一些问题
java.util.Observable的黑暗面
如同你所发现的,可观察者是一个“类”而不是一个“接口”,更糟的是,它甚至没有实现一个接口。
- 首先,因为Observable是一个“类”,你必须设计一个类继承它。如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟Java不支持多重继承。 这限制了Observable的复用潜力。
- 因为没有Observable接口,所以你无法建立自己的实现,和Java内置的Observer API搭配使用,也无法将java.util的实现换成另一套做法的实现。
- 你会发现setChanged()方法被保护起来了(被定义成protected),这意味着:除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象中来。这个设计违反了第二个设计原则:“多用组合,少用继承”。
package Observable;
public interface DisplayElement {
public void display();
}
package Observable;
import java.util.Observable;
/**
* 我们不再需要追踪观察者了,也不需要管理注册与删除(让超类代劳即可)。所以我们把注册、添加、通知的相关代码删除。
*/
public class WeatherDate extends Observable {
private float temperature;
private float humidity;
private float pressure;
//我们的构造器不再需要为了记住观察者们而建立数据结构了。
public WeatherDate() {}
//在调用notifuObservers()之前,要先调用setChanged()来指示状态已经改变
public void measurementsChanged(){
setChanged();
//注意:我们没有调用notifyObservers()传送数据对象,这表示我们采用的做法是“拉”。
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
//这些并不是新方法,只是因为我们要使用“拉”的做法,所以才提醒你有这些方法。察者会利用这些方法取得WeatherData对象的状态。
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
package Observable;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private Observable observable;
private float temperature;
private float humidity;
//现在构造器需要一Observable当参数,并将CurrentCondi- tionsDisplay对象登记成为观察者。
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
//改变update()方法,增加Observable和数据对象作为参数。
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherDate){
WeatherDate weatherDate = (WeatherDate)obs;
this.temperature = weatherDate.getTemperature();
this.humidity = weatherDate.getHumidity();
display();
}
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
package Observable;
import java.util.Observable;
import java.util.Observer;
public class ForecastDisplay implements Observer, DisplayElement {
private float curPre = 29.92f;
private float lastPre = 0;
private Observable observable;
public ForecastDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherDate){
WeatherDate weatherDate = (WeatherDate)obs;
lastPre = curPre;
curPre = weatherDate.getHumidity();
display();
}
}
@Override
public void display() {
System.out.print("Forecast: ");
if (curPre > lastPre) {
System.out.println("Improving weather on the way!");
} else if (curPre == lastPre) {
System.out.println("More of the same");
} else if (curPre < lastPre) {
System.out.println("Watch out for cooler, rainy weather");
}
}
}
package Observable;
import java.util.Observable;
import java.util.Observer;
public class HeatIndexDisplay implements Observer, DisplayElement {
private float heatIndex = 0;
private Observable observable;
public HeatIndexDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherDate){
WeatherDate weatherDate = (WeatherDate)obs;
heatIndex = computeHeatIndex(weatherDate.getTemperature(), weatherDate.getHumidity());
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;
}
@Override
public void display() {
System.out.println("Heat index is " + heatIndex);
}
}
package Observable;
import java.util.Observable;
import java.util.Observer;
public class StatisticsDisplay implements Observer, DisplayElement{
private float max = 0.0f;
private float min = 200;
private float sum = 0.0f;
private int num = 0;
private Observable observable;
public StatisticsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherDate){
WeatherDate weatherDate = (WeatherDate)obs;
sum += weatherDate.getTemperature();
num++;
if (weatherDate.getTemperature() < min){
min = weatherDate.getTemperature();
}
if (weatherDate.getTemperature() > max){
max = weatherDate.getTemperature();
}
display();
}
}
@Override
public void display() {
System.out.println("Avg/Max/Min temperature = " + (sum / num)
+ "/" + max + "/" + min);
}
}
测试
package Observable;
public class WeatherStation {
public static void main(String[] args) {
WeatherDate weatherDate = new WeatherDate();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherDate);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherDate);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherDate);
HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherDate);
weatherDate.setMeasurements(80, 65, 30.4f);
weatherDate.setMeasurements(82, 70, 29.2f);
weatherDate.setMeasurements(10, 10, 29.2f);
}
}
总结
- 观察者模式定义了对象之间一 对多的关系。
- 主题(也就是可观察者)用一个共同的接口来更新观察者
- 观察者和可观察者之间用松耦合方式结合(loosecoupling),可观察者不知道观察者的细节,只知道观察者实现 了观察者接口
- 使用此模式时,你可从被观察者处推(push)或拉(pull) 数据(然而,推的方式被认为 更“正确”)。
- 有多个观察者时,不可以依赖 特定的通知次序。
- Java有多种观察者模式的实 现,包括了通用的java.util. Observable。
- 要注意java.util.Observable实 现上所带来的一些问题。
- 如果有必要的话,可以实现自己的Observable,这并不难, 不要害怕。
- Swing大量使用观察者模式, 许多GUI框架也是如此。
- 此模式也被应用在许多地方, 例如:JavaBeans、RMI。