气象站系统:气象站可以通过物理装置取得气象信息,WeatherData对象可以通过气象站提供的外部接口获取气象信息。
需求:WeatherData获取最新数据时,需要实时更新三个布告板的气象数据。三个布告板分别是:目前状况、气象统计、天气预报。
一、整理分析:
1.WeatherData有三个getter方法getTemperature(),getHumidity(),getPressure()分别获取温度、湿度、气压值;
2.当数据改变时,WeatherData有个方法measurementsChanged()会被调用(我们不关心如何被调用),此时需更新三个布告板的气象信息;
3.系统需可扩展,可随时新增或者删除布告板。
第一次尝试:
1 public class WeatherData { 2 3 public void measurementsChanged() { 4 float temperature = getTemperature(); 5 float humidity = getHumidity(); 6 float pressure = getPressure(); 7 //目前状况布告板更新 8 currentConditionsDisplay.update(temperature, humidity, pressure); 9 //气象统计布告板更新 10 statisticsDisplay.update(temperature, humidity, pressure); 11 //天气预报布告板更新 12 forecastDisplay.update(temperature, humidity, pressure); 13 } 14 15 //WeatherData的其他方法 16 17 }
存在的问题:针对实现编程,会难以扩展和维护,如果需要新增或者删除布告板,必须修改该程序。
例如报社,订阅报纸后,报社会定期给你邮寄报纸,停止订阅后报社便停止邮寄。我们把报社改称为“主题 Subject”,把订阅者改成为“观察者 Observer”来研究观察者模式。
观察者模式定义:定义了对象之间的一对多依赖,这样一来,当一个对象改变时,它的所有依赖者都会收到通知并自动更新。
主题不需要知道观察者具体是谁,只需要知道观察者实现了Observer接口,并不关心观察者具体做了哪些事,这种交互对象间的松耦合是设计中追求的原则,下面看主题Subject和观察者Observer接口代码:
1 /*观察者*/ 2 public interface Observer { 3 public void update(float temp, float humidity, float pressure); 4 }
1 /*主题*/ 2 public interface Subject { 3 /*注册成为观察者*/ 4 public void registerObserver(Observer o); 5 /*移除观察者*/ 6 public void removeObserver(Observer o); 7 /*通知所有观察者*/ 8 public void notifyObservers(); 9 }
1 public interface DisplayElement { 2 /** 3 * 布告板都实现该接口,调用display来展示气象信息 4 */ 5 public void display(); 6 }
现在让WeatherData实现主题接口:
1 public class WeatherData implements Subject { 2 private List<Observer> observerList; 3 private float temperature; 4 private float humidity; 5 private float pressure; 6 7 public WeatherData() { 8 observerList = new ArrayList<Observer>(); 9 } 10 public void registerObserver(Observer o) { 11 observerList.add(o); 12 } 13 14 public void removeObserver(Observer o) { 15 int i = observerList.indexOf(o); 16 if (i >= 0) { 17 observerList.remove(i); 18 System.out.println(o.getClass().getName() + " is removed."); 19 } 20 } 21 22 public void notifyObservers() { 23 for (Observer o : observerList) { 24 o.update(temperature, humidity, pressure); 25 } 26 } 27 /*数据更新时,通知所有观察者*/ 28 public void measurementsChanged() { 29 notifyObservers(); 30 } 31 32 }
布告板实现观察者接口:
1 /*目前状况布告板*/ 2 public class CurrentConditionsDisplay implements Observer, DisplayElement { 3 4 private float temperature; 5 private float humidity; 6 private Subject weatherData; 7 8 public CurrentConditionsDisplay(Subject subject) { 9 this.weatherData = subject; 10 weatherData.registerObserver(this); 11 } 12 13 public void removeObserver() { 14 weatherData.removeObserver(this); 15 } 16 17 public void update(float temp, float humidity, float pressure) { 18 this.temperature = temp; 19 this.humidity = humidity; 20 display(); 21 } 22 23 public void display() { 24 System.out.println("CurrentConditionsDisplay conditions:" + temperature + "F degrees and " + humidity + "% humidity"); 25 } 26 27 } 28 29 /*天气预报布告板*/ 30 public class ForecastDisplay implements Observer, DisplayElement { 31 32 private float temperature; 33 private float pressure; 34 private Subject weatherData; 35 36 public ForecastDisplay(Subject subject) { 37 this.weatherData = subject; 38 weatherData.registerObserver(this); 39 } 40 41 public void removeObserver() { 42 weatherData.removeObserver(this); 43 } 44 45 public void update(float temp, float humidity, float pressure) { 46 this.temperature = temp; 47 this.pressure = pressure; 48 display(); 49 } 50 51 public void display() { 52 System.out.println("ForecastDisplay conditions:" + temperature + "F degrees and " + pressure + "Pa"); 53 } 54 55 } 56 57 /*气象状况布告板略*/
代码测试:
1 public class ObserverTest { 2 3 @Test 4 public void test() { 5 WeatherData weatherDate = new WeatherData(); 6 CurrentConditionsDisplay ccDisplay = new CurrentConditionsDisplay(weatherDate); 7 ForecastDisplay fcDisplay = new ForecastDisplay(weatherDate); 8 weatherDate.setMeasurements(12.3f, 38.6f, 130f); 9 ccDisplay.removeObserver(); 10 weatherDate.setMeasurements(12.3f, 38.6f, 130f); 11 fcDisplay.removeObserver(); 12 weatherDate.setMeasurements(12.3f, 38.6f, 130f); 13 System.out.println("notify ok"); 14 } 15 }
运行结果:
1 CurrentConditionsDisplay conditions:12.3F degrees and 38.6% humidity 2 ForecastDisplay conditions:12.3F degrees and 130.0Pa 3 com.project.design.observer.CurrentConditionsDisplay is removed. 4 ForecastDisplay conditions:12.3F degrees and 130.0Pa 5 com.project.design.observer.ForecastDisplay is removed. 6 notify ok
Java JDK中也存在已经定义好的观察者模式,在java.util包中(Observable(主题), Observer(观察者)),大家有兴趣可以自己了解下。java中的观察者可以手动从主题中拉取数据,主题也可以主动去推送数据。