今天把第二章的观察者模式看完了,收获挺大,感觉这本书买的还是挺直的。
本章又有了一个新的设计原则:为了交互对象之间的松耦合设计而努力。
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
现整理书中关于观察者模式的代码如下:
一、自己设计观察者模式实现根据气象站的数据实时更新布告栏
1、被观察者接口Subject.java
package com.sq.observerPattern.weather; /** * 主题接口 * @author SQ * */ public interface Subject { /** * 注册观察者 * @param o */ public void registerObserver(Observer o); /** * 移除一个注册的观察者 * @param o */ public void removeObserver(Observer o); /** * 通知所有注册了的观察者主题的数据更新了 */ public void notifyObservers(); }
package com.sq.observerPattern.weather; import java.util.ArrayList; /** * 实际的被观察者 * @author SQ * */ public class WeatherData implements Subject{ private ArrayList<Observer> observers; //记录注册的观察者 private float temperature; //温度 private float humidity; //湿度 private float pressure; //气压 public WeatherData() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if(i >= 0){ observers.remove(i); } } @Override public void notifyObservers() { for(int i=0; i<observers.size(); i++){ Observer observer = observers.get(i); observer.update(temperature, humidity, pressure); } } /** * 当从气象站得到更新观测值时通知观察者 */ public void measurementsChanged() { notifyObservers(); } /** * 获取更新的观测值 * @param temperature 温度 * @param humidity 湿度 * @param pressure 气压 */ public void setMeasurements(float temperature, float humidity, float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
package com.sq.observerPattern.weather; /** * 观察者接口 * @author SQ * */ public interface Observer { public void update(float temperature, float humidity, float pressure); }
package com.sq.observerPattern.weather; /** * 显示布告栏内容的接口 * @author SQ * */ public interface DisplayElement { /** * 在布告栏中显示相应的内容 */ public void display(); }
package com.sq.observerPattern.weather; /** * 目前天气状态布告栏 * @author SQ * */ public class CurrentConditionDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity."); } @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } }
package com.sq.observerPattern.weather; /** * 天气预报布告栏 * @author SQ * */ 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"); } } }
package com.sq.observerPattern.weather; /** * 气象统计布告栏 * @author SQ * */ 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); } }
package com.sq.observerPattern.weather; /** * 酷热指数布告栏 * @author SQ * */ 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); } }
package com.sq.observerPattern.weather; /** * 测试程序,即气象站 * @author SQ * */ public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionDisplay cunrrentDisplay = new CurrentConditionDisplay(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); } }
10、输出结果
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
1、WeatherData.java
package com.sq.observerPattern.weatherObservable; import java.util.ArrayList; import java.util.Observable; public class WeatherData extends Observable{ private float temperature; //温度 private float humidity; //湿度 private float pressure; //气压 public WeatherData() { } /** * 当从气象站得到更新观测值时通知观察者 */ public void measurementsChanged() { setChanged(); notifyObservers(); } /** * 获取更新的观测值 * @param temperature 温度 * @param humidity 湿度 * @param pressure 气压 */ 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; } }
package com.sq.observerPattern.weatherObservable; import java.util.Observable; import java.util.Observer; /** * 目前天气状态布告栏 * @author SQ * */ public class CurrentConditionDisplay implements Observer, DisplayElement { Observable observable; private float temperature; private float humidity; public CurrentConditionDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity."); } @Override public void update(Observable o, Object arg) { if(o instanceof WeatherData){ WeatherData weatherData = (WeatherData) o; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } }
package com.sq.observerPattern.weatherObservable; import java.util.Observable; import java.util.Observer; /** * 天气预报布告栏 * @author SQ * */ public class ForecastDisplay implements Observer, DisplayElement{ Observable observable; private float currentPressure = 29.92f; private float lastPressure; public ForecastDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override 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"); } } @Override public void update(Observable o, Object arg) { if(o instanceof WeatherData){ WeatherData weatherData = (WeatherData) o; lastPressure = currentPressure; currentPressure = weatherData.getPressure(); display(); } } }
package com.sq.observerPattern.weatherObservable; import java.util.Observable; import java.util.Observer; /** * 气象统计布告栏 * @author SQ * */ public class StatisticsDisplay implements Observer, DisplayElement{ Observable observable; private float maxTemp = 0.0f; private float minTemp = 200; private float tempSum= 0.0f; private int numReadings; public StatisticsDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if(o instanceof WeatherData){ WeatherData weatherData = (WeatherData) o; float temp = weatherData.getTemperature(); 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); } }
package com.sq.observerPattern.weatherObservable; import java.util.Observable; import java.util.Observer; /** * 酷热指数布告栏 * @author SQ * */ public class HeatIndexDisplay implements Observer, DisplayElement{ Observable observable; float heatIndex = 0.0f; public HeatIndexDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if(o instanceof WeatherData){ WeatherData weatherData = (WeatherData) o; heatIndex = computeHeatIndex(weatherData.getTemperature(), weatherData.getHumidity()); } } 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); } }
package com.sq.observerPattern.weatherObservable; /** * 测试程序,即气象站 * weatherObservable包中是使用了java内置的观察者模式实现的气象站 * WeatherData继承了系统的可观察者Observable类,而观察者们继承了系统的观察者Observer接口 * @author SQ * */ public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionDisplay cunrrentDisplay = new CurrentConditionDisplay(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); } }
Forecast: Improving weather on the way! Avg/Max/Min temperature = 80.0/80.0/80.0 Current conditions: 80.0F degrees and 65.0% humidity. Forecast: Watch out for cooler, rainy weather Avg/Max/Min temperature = 81.0/82.0/80.0 Current conditions: 82.0F degrees and 70.0% humidity. Forecast: More of the same Avg/Max/Min temperature = 80.0/82.0/78.0 Current conditions: 78.0F degrees and 90.0% humidity.
使用Java内置的观察者模式的输出结果和我们之前的文字的输出次序不一样,这就使得我们在一些场合下不能依赖于观察者被通知的次序。
不知道你看出来没有Observable是一个类,而不是一个接口,而且它甚至没有实现一个借口,这样我们使用时就必须继承它,但如果我还有一个类要继承,那怎么办,Java并不支持多重继承。它违反了“针对接口编程,而非针对实现编程”的设计原则。而且Observable把它关键的方法setChanged()保护了起来(设为了protected),这样我们也不能使用组合的方式使用它,它违反了“多用组合,少用继承”的设计原则。