观察者模式的定义
观察者模式定义了对象之间的一对多依赖,这样,当一个对象改变状态的时候,它的所有依赖者都会收到通知并自动更新。
场景
Weather-O—Rama的Internet气象观测站
场景描述
该气象站建立在该公司的专利WeatherData
类上,WeatherData
类负责跟物理气象站联系,以获得最新的天气状况(温度、湿度、气压),然后实时更新各种显示设备。目前的显示设备有三种:a)显示目前状况(温度、湿度、气压)的设备,b)显示气象统计的设备,c)显示天气预报的设备;WeatherData
类还需要具备方便未来有其它显示设备以插拔的方式接入的功能。
已有WeatherData
类,其中
getTemperature()
、getHumidity()
、getPressure()
是气象站的开发人员已经实现好的方法;
- 我们只需要实现
measurementsChanged()
,该方法一旦气象测量有更新就会被调用;
不好的设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class WeatherData {
public void measurementsChanged() { float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure); statisticsDisplay.update(temp, humidity, pressure); forecastDisplay.update(temp, humidity, pressure); }
}
|
解决方案一
相关接口:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
public interface Observer { public void update(float temp, float humidity, float pressure); }
public interface DisplayElement { public void display(); }
|
实现WeatherData
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class WeatherData implements Subject { private List<Observer> observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList<Observer>(); } public void registerObserver(Observer o) { observers.add(o); } public void removeObserver(Observer o) { observers.remove(o); } public void notifyObservers() { for (Observer observer : observers) { 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; } }
|
实现显示设备:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private WeatherData weatherData; public CurrentConditionsDisplay(WeatherData 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"); } }
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); } }
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"); } } }
|
观测站运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class WeatherStation { 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);
weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); weatherData.removeObserver(forecastDisplay); weatherData.setMeasurements(62, 90, 28.1f); } }
|
解决方案二:使用Java内置观察者模式
注意点:
java.util.Observable
是类;
java.util.Observable
中的setChanged()
方法;
参考
- https://wickedlysmart.com/head-first-design-patterns/