观察者模式(二)--《Head First DesignPattern》
我们用Java中自带的观察者模式接口来重写前面的例子。
先看一下类图:
这里用到了一个setChanged函数,它用来标记状态已经改变的事实,好让notifyObservers()知道当它调用时就应该更新观察者。如果调用notifyObservers()之前没有先调用setChanged(),观察者就不会被通知到。setChanged()方法可以让你在更新观察者时,有更大的弹性,你可以适当地通知观察者。
Observable内部是这样的结构:
setChanged(){ changed = true; } notifyObservers(Object arg){ if (changed){ for every observer on the list{ call update(this, arg)
} } } notifyObservers(){ notifyObservers(null); }
注意这里是继承Observable类,而不是接口
1 package headfirst.observer.weatherobservable; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 public class WeatherData extends Observable { 7 private float temperature; 8 private float humidity; 9 private float pressure; 10 11 public WeatherData() { } 12 13 public void measurementsChanged() { 14 //设置changed变量 15 setChanged(); 16 notifyObservers(); 17 } 18 19 public void setMeasurements(float temperature, float humidity, float pressure) { 20 this.temperature = temperature; 21 this.humidity = humidity; 22 this.pressure = pressure; 23 measurementsChanged(); 24 } 25 26 public float getTemperature() { 27 return temperature; 28 } 29 30 public float getHumidity() { 31 return humidity; 32 } 33 34 public float getPressure() { 35 return pressure; 36 } 37 } 38
观察者实现Observer接口,
1 package headfirst.observer.weatherobservable; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 public class CurrentConditionsDisplay implements Observer, DisplayElement { 7 Observable observable; 8 private float temperature; 9 private float humidity; 10 11 public CurrentConditionsDisplay(Observable observable) { 12 this.observable = observable; 13 observable.addObserver(this); 14 } 15 16 public void update(Observable obs, Object arg) { 17 if (obs instanceof WeatherData) { 18 WeatherData weatherData = (WeatherData)obs; 19 this.temperature = weatherData.getTemperature(); 20 this.humidity = weatherData.getHumidity(); 21 display(); 22 } 23 } 24 25 public void display() { 26 System.out.println("Current conditions: " + temperature 27 + "F degrees and " + humidity + "% humidity"); 28 } 29 }
ForecastDisplay
1 package headfirst.observer.weatherobservable; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 public class ForecastDisplay implements Observer, DisplayElement { 7 private float currentPressure = 29.92f; 8 private float lastPressure; 9 10 public ForecastDisplay(Observable observable) { 11 observable.addObserver(this); 12 } 13 14 public void update(Observable observable, Object arg) { 15 if (observable instanceof WeatherData) { 16 WeatherData weatherData = (WeatherData)observable; 17 lastPressure = currentPressure; 18 currentPressure = weatherData.getPressure(); 19 display(); 20 } 21 } 22 23 public void display() { 24 System.out.print("Forecast: "); 25 if (currentPressure > lastPressure) { 26 System.out.println("Improving weather on the way!"); 27 } else if (currentPressure == lastPressure) { 28 System.out.println("More of the same"); 29 } else if (currentPressure < lastPressure) { 30 System.out.println("Watch out for cooler, rainy weather"); 31 } 32 } 33 }
1 package headfirst.observer.weatherobservable; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 public class StatisticsDisplay implements Observer, DisplayElement { 7 private float maxTemp = 0.0f; 8 private float minTemp = 200; 9 private float tempSum= 0.0f; 10 private int numReadings; 11 12 public StatisticsDisplay(Observable observable) { 13 observable.addObserver(this); 14 } 15 16 public void update(Observable observable, Object arg) { 17 if (observable instanceof WeatherData) { 18 WeatherData weatherData = (WeatherData)observable; 19 float temp = weatherData.getTemperature(); 20 tempSum += temp; 21 numReadings++; 22 23 if (temp > maxTemp) { 24 maxTemp = temp; 25 } 26 27 if (temp < minTemp) { 28 minTemp = temp; 29 } 30 31 display(); 32 } 33 } 34 35 public void display() { 36 System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) 37 + "/" + maxTemp + "/" + minTemp); 38 } 39 }
main函数
1 package headfirst.observer.weatherobservable; 2 3 public class WeatherStation { 4 5 public static void main(String[] args) { 6 WeatherData weatherData = new WeatherData(); 7 CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(weatherData); 8 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); 9 ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); 10 11 weatherData.setMeasurements(80, 65, 30.4f); 12 weatherData.setMeasurements(82, 70, 29.2f); 13 weatherData.setMeasurements(78, 90, 29.2f); 14 } 15 }
这里还是要注意几点:
- java.util.Observable是一个“类”而不是一个“接口”,所以我们的类必须继承它,这带来的问题就是Java中只有单继承,所以这限制了Observable的复用能力。而且Observable中的setChanged是被设置为protected的,所以除非你继承自Observable,否则你无法创建Observable实例并把它组合到我们的对象中来。如果实在不行,还是建立我们自己实现一套自己的接口,类似于第一篇所说的。
- Java中的Swing的API就用到了观察者模式。例如我们有一个JButton对象,然后给他设置监听器。这里的监听器就是一个Observer,而JButton就是一个主题。当在JButton上有对应的事件发生的时候,例如点击,那么就会通知监听器,调用类似于update的方法,其实就是ActionListener中的actionPerformed()方法。