Java设计模式--观察者模式
2017-11-09 14:35
295 查看
观察者模式
参考《Head First设计模式》中的观察者模式完成。气象监测应用需求
观察者模式介绍
手写观察者模式
Java内置的观察者模式
气象监测应用需求
根据气象站实时输出的湿度、温度和气压值制作三块布告板。第一块布告板实时显示当前的温度、湿度和气压;第二块布告板显示当日的平均温度、最低温度以及最高温度;第三块布告板根据天气显示预报信息。气象站提供了WeatherData类来获得实时测量的温度、湿度和气压值。
气象站提供的接口如上如所示,三个getter方法用于获取温度、湿度和气压;每当气象测量值变化,就会调用measurementsChanged()方法,实现measurementsChanged()就是我们的工作。
分析:
考虑到需求中布告板显示的内容可能会发生变化,为了方便日后修改程序,希望每次只需要添加一块新的布告板,而其他程序不需要变化,努力做到交互对象之间松耦合。这个需求中,天气测量值一旦变化,三个布告板要及时获得改变并展示,完全符合观察者模式。
观察者模式介绍
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。观察者模式中有主题和观察者两个对象,以报纸的订阅为例简述观察者模式。
报社的任务就是出版报纸;
向某家报社订阅报纸,只要有新报纸出版,报社就会给你送来。只要你订阅了该报纸,就会一直收到新报纸;
当你不想看该报纸时,取消订阅,他们就不会再送报纸来;
只要报社还在运营,就一直有人向他们订阅或取消订阅报纸。
把订阅报纸类比为观察者模式,出版者就是“主题”,订阅者就是“观察者”。在该需求中,气象站就是一个“主题”,而每块布告板就是一个“观察者”。每个布告板(观察者)向气象站(主题)注册,就可以在测量值变化时获得消息;取消某布告板类比为“观察者”向“主题”注销;新增一块布告板类比为新增一个气象“主题”的“观察者”。
手写观察者模式
面向对象的设计原则:针对接口编程,不针对实现编程。根据上述原则,把主题和观察者分别抽象为Subject和Observer接口。并添加一个DisplayElement接口用于展示。
Observer接口中只有一个update()方法,用于当主题变化时执行。
Subject接口中有使得观察者订阅的方法registerObserver();当观察者不想接收信息时的取消订阅方法removeObserver();以及当主题数据发生改变时通知订阅该主题的所有观察者的notifyObserver()方法。实际的主题,例如本需求中的WeatherData需要实现该接口,内部维护一个观察者数组。registerObserver()方法中只需要把观察者加入自身的观察者数组;removeObserver()方法中把要取消订阅的观察者移除;notifyObserver()则是遍历当前的观察者数组,依次调用每个观察者的update()即可。
Subject接口
/** * Created by Janet on 2017/11/6. * 观察者模式中主题的接口 */ public interface Subject { public void registerObserver(Observer o);//观察者o订阅主题 public void removeObserver(Observer o);//观察者o取消订阅主题 public void notifyObserver();//主题通知观察者 }
Observer接口
/** * Created by Janet on 2017/11/6. * 观察者模式中观察者的接口 */ public interface Observer { public void update(float temp,float humidity,float pressure);//主题传来通知 }
用于展示的接口
/** * Created by Janet on 2017/11/9. * 展示结果的接口 */ public interface DisplayElement { public void display(); }
WeatherData主题
import java.util.ArrayList; /** * Created by Janet on 2017/11/6. * 气象预报的主题 */ public class WeatherData implements Subject { private ArrayList observers;//观察者列表 private float temperature;//温度 private float humidity;//湿度 private float pressure;//气压 //每次设置温湿度和气压,主题都会发生改变 public void setTemperature(float temperature,float humidity,float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } public WeatherData(){ observers = new ArrayList(); } //主题发生改变时执行的方法 public void measurementsChanged(){ notifyObserver(); } //观察者o订阅主题 @Override public void registerObserver(Observer o) { observers.add(o); } //观察者o取消订阅主题 @Override public void removeObserver(Observer o) { int index = observers.indexOf(o); if( index >= 0 ){ observers.remove(o); } } //主题通知所有订阅者 @Override public void notifyObserver() { for(int i = 0;i<observers.size();i++){ Observer o = (Observer) observers.get(i); o.update(temperature,humidity,pressure); } } }
观察者1–展示温度湿度气压的布告板
/** * Created by Janet on 2017/11/6. * 第一块展示温湿度的布告板 */ public class CurrentConditionDisplay implements Observer,DisplayElement { private Subject weatherData; private float temperature; private float humidity; //一创建就向主题注册 public CurrentConditionDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void display() { System.out.println("Current condition : "+temperature+"F degrees and "+humidity+"% humidity"); } //主题变化时执行的函数 @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } }
观察者2–展示温度最大最小及平均值的布告板
/** * Created by Janet on 2017/11/6. * 第二块展示最小,平均,最大温度的布告板 */ public class StatisticsDisplay implements Observer,DisplayElement { private float minTemperature = Float.MAX_VALUE;//最小温度 private float avgTemperature;//平均温度 private float maxTemperature = Float.MIN_VALUE;//最大温度 private int num = 0;//用于计算平均温度 private Subject weatherData; //创建观察者时要向主题注册 public StatisticsDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void display() { System.out.println("Avg/Max/Min temperature = "+avgTemperature+"/"+maxTemperature+"/"+minTemperature); } //主题变化时执行的函数 @Override public void update(float temp, float humidity, float pressure) { if( minTemperature > temp ){ minTemperature = temp; } if( maxTemperature < temp ){ maxTemperature = temp; } avgTemperature = (avgTemperature * num + temp)/(num+1); num++; display(); } }
测试函数
/** * Created by Janet on 2017/11/6. */ public class WeatherStation { public static void main(String[] args){ WeatherData weatherData = new WeatherData(); CurrentConditionDisplay o1 = new CurrentConditionDisplay(weatherData);//第一块布告板 StatisticsDisplay o2 = new StatisticsDisplay(weatherData); weatherData.setTemperature(19,20,1000); weatherData.setTemperature(13,15,1500); weatherData.setTemperature(0,22,1000); } }
执行结果如下:
Java内置的观察者模式
java.util包中的Observable是主题的超类(注意,java内置的主题是类不是接口),Observer是观察者的接口。实际主题需要继承超类Observeable,已经写好了观察者订阅,取消订阅以及通知的方法。其中setChanged()方法用于当主题数据修改时,用来标记状态已经改变。WeatherData内部的notifyObservers()方法会首先判断标志位是否更改,再通知各观察者。
观察者获取主题变化的数据实际上有“推”和“拉”两种方式。“推”表示主题数据发生变化时,主动把变化的数据推送给订阅的观察者们;“拉”表示当观察者需要时主动向主题索取数据。上文中我们只是自己实现了主题“推数据”的方法。Java内置的观察者模式支持“推”和“拉”两种获取数据的模式。
利用Java内置实现WeatherData
注意,使用Java内置主题时,notifyObservers()有两种重载方法:notifyObservers()和notifyObservers(Object arg);notifyObservers(Object arg)可以传送指定数据给观察者。notifyObservers()用于拉数据的模式,notifyObservers(Object arg)用于推的模式。
import java.util.Observable; /** * Created by Janet on 2017/11/9. * 使用java内置类实现主题 */ public class WeatherData extends Observable{//继承Observable,内部实现了主题的创建观察者列表等方法 private float temperature; private float humidity; private float pressure; public WeatherData(){}//此处无需自行创建观察者列表,超类已经创建 public void setMeasurements(float temperature,float humidity,float pressure){ this.pressure = pressure; this.humidity = humidity; this.temperature = temperature; measurementsChanged();//数据改变后调用该方法 } private void measurementsChanged() { setChanged();//设置改变标志位 notifyObservers();//没有参数传入,说明是观察者向主题索取数据 } public float getTemperature(){ return temperature; } public float getHumidity(){ return humidity; } public float getPressure(){ return pressure; } }
利用Java内置实现观察者
注意,Java内置的观察者获取主题动态有“推”和“拉”两种模式。推即为主题import java.util.Observable; import java.util.Observer; /** * Created by Janet on 2017/11/9. * 使用java内置类实现观察者 */ public class CurrentConditionsDisplay implements Observer,DisplayElement { private Observable observable;//观察者内部记录主题 private float temperature; private float humidity; //构造函数中把观察者加入到主题中 public CurrentConditionsDisplay(Observable observable){ this.observable = observable; observable.addObserver(this);//主题把观察者加入到列表 } @Override public void display() { System.out.println("Current conditions: "+temperature+" F degrees and "+humidity+"% humidity"); } //不同的观察者索取的数据由update方法展示 @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(); } } }
import java.util.Observable; import java.util.Observer; /** * Created by Janet on 2017/11/9. */ public class StatisticsDisplay implements Observer,DisplayElement { private Observable observable; private float minTemperature = Float.MAX_VALUE;//最小温度 private float avgTemperature;//平均温度 private float maxTemperature = Float.MIN_VALUE;//最大温度 private int num = 0;//用于计算平均温度 public StatisticsDisplay(Observable observable){ this.observable = observable; observable.addObserver(this); } @Override public void display() { System.out.println("Avg/Max/Min temperature = "+avgTemperature+"/"+maxTemperature+"/"+minTemperature); } @Override public void update(Observable o, Object arg) { if( o instanceof WeatherData ){ WeatherData weatherData = (WeatherData) o; float temp = weatherData.getTemperature(); if( minTemperature > temp ){ minTemperature = temp; } if( maxTemperature < temp ){ maxTemperature = temp; } avgTemperature = (avgTemperature * num + temp)/(num+1); num++; display(); } } }
import java.util.Observable; import java.util.Observer; /** * Created by Janet on 2017/11/9. */ public class ForecastDisplay implements Observer,DisplayElement { private Observable observable;//主题 private float currentPressure = 29.92f; private float lastPressure; public ForecastDisplay(Observable observable){ this.observable = observable; observable.addObserver(this); } @Override public void display() { if( this.currentPressure < this.lastPressure ){ System.out.println("气压变小"); }else if( this.currentPressure > this.lastPressure ){ System.out.println("气压变大"); }else{ System.out.println("气压不变"); } } @Override public void update(Observable o, Object arg) { if( o instanceof WeatherData ){ WeatherData weatherData = (WeatherData) o; this.lastPressure = currentPressure; this.currentPressure = weatherData.getPressure(); display(); } } }
测试函数
/** * Created by Janet on 2017/11/9. */ public class WeatherStation { public static void main(String[] args){ WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); weatherData.setMeasurements(19,20,1000); weatherData.setMeasurements(13,15,1500); weatherData.setMeasurements(0,22,1000); } }
运行结果如下:
Java内置和手写观察者模式的区别
Java内置的主题采用类的形式,扩展性不如接口;Java内置观察者创建的顺序不等同于主题改变时通知的顺序,在上例子中可见,而自写的主题内部维护观察者数组采用有序的ArrayList,可以保证顺序。
相关文章推荐
- java设计模式-观察者模式
- Java 设计模式——观察者模式
- java设计模式---观察者设计模式
- java设计模式--观察者模式
- java设计模式-观察者模式
- java设计模式之观察者模式
- Java设计模式之观察者模式
- Java设计模式--观察者模式
- Java设计模式之观察者模式
- java设计模式之观察者模式
- Java设计模式(15)行为型:观察者模式
- Java设计模式之观察者模式
- Java设计模式-观察者模式
- java设计模式之观察者模式
- 详解java的事件监听机制和观察者设计模式
- Java设计模式之观察者模式
- java设计模式——行为型模式专题(一)观察者模式
- Java设计模式之观察者模式简单介绍
- Java23种设计模式案例:观察者模式(observer)