设计模式学习专栏三--------观察者模式
场景
建立一个气象站应用, 利用WeatherData对象取得数据,并更新三个布告板: 目前状况 , 气象统计 , 天气预报
此系统中的三个部分是气象站(获取实际气象数据的物理装置) , WeatherData对象(追踪来自气象站的数据,并更新布告板) 和 布告板(显示目前天气状况给用户看.)
WeatherData对象知道如何跟物理气象站联系,以取得更新的数据.WeatherData对象会随即更新三个布告板的显示: 目前状况(温度 , 湿度 , 气压), 气象统计和天气预报
刚开始实现方式
//WeatherData构造函数 , 当新公告板加入时, 也要跟着改变
public WeatherData(CurrentConditionsDisplay currentConditionsDisplay,
StatisticsDisplay statisticsDisplay,ForecastDisplay forecastDisplay) {
this.currentConditionsDisplay = currentConditionsDisplay;
this.statisticsDisplay = statisticsDisplay;
this.forecastDisplay = forecastDisplay;
}
如何解决
分析可变部分
在weatherData的构造函数中 , 对布告板的加入与移除
在收到气象数据变化时, 需要通知所有已加入的布告板
观察者模式总览
定义:在
对象之间定义一对多的依赖
,这样一来, 当一个对象改变状态,依赖它的对象都会收到通知,并自动更新
-
类图
-
模式的理解
-
角色
- 主题Subject : 主题对象
管理某些数据
, 当主题内的数据改变时 , 就会通知观察者
- 观察者Observer : 观察者已经
订阅
主题, 以便在主题数据改变时能收到更新 , 当不想订阅主题时,也能随时移除订阅
- 主题Subject : 主题对象
-
细节
使用观察者模式在任意时候都能 增加/删除新的观察者,而不修改原有代码
-
观察者模式 对数据推送有两种方式
推(push)和拉(pull)
- 推送方式:
- 当主题通知变化时, 主题携带着数据通知观察者.
- 拉取方式
- 当主题通知变化时, 观察者通过主题的引用 , 去获取主题中的数据信息
- 推送方式:
-
观察者模式提供了一种对象设计,
让主题和观察者之间松耦合
- 对主题而言, 它只知道观察者实现了某个接口(Observer). 而不需要知道观察者的具体实现是谁, 做了些什么与其他细节
- 对观察者而言, 它不需要关心主题细节, 只知道可以注册/移除主题 , 并且主题有数据更新时会调用update方法传入数据
-
核心代码部分
-
主题
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); }
-
WeatherData
public class WeatherData implements Subject { private ArrayList<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) { int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } } public void notifyObservers() { //遍历所有的观察者,调用update方法 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(); } //其他的get方法 }
-
观察者--今日布告板
public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; //持有主题引用 , 用于拉取数据/删除主题 public CurrentConditionsDisplay(Subject 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 WeatherStationHeatIndex { 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); } }
-
输出结果
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! 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 Current conditions: 78.0F degrees and 90.0% humidity Avg/Max/Min temperature = 80.0/82.0/78.0 Forecast: More of the same
Java内置的观察者模式
主题 ==> java.util.Observable
观察者 ==>
java.util.Observer
-
类图
-
使用
把对象变成观察者 , 实现Observer接口, 使用addObserver()或者deleteObserver()进行 订阅/删除主题
-
可观察者(主题)送出通知
-
调用setChanged()方法, 标记数据已改变
setChanged(){ changed = true; } notifyObservers(Object arg){ if(changed){ //只有当changed为true时才进行通知 for every observer on the list{ call update(this,arg) } changed = false; //通知观察者后,将changed设置为false } } notifyObservers(){ notifyObservers(null); }
-
调用notifyObservers() 通知所有观察者
- notifyObservers();
- notifyObservers(Object args);
-
-
观察者接收通知
- update(Observable o , Object arg) //拿到可观察者引用,即可拉取数据
public void update(Observable obs, Object arg) { if (obs instanceof WeatherData) { WeatherData weatherData = (WeatherData)obs; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } }
参考
书籍: HeadFirst设计模式
代码参考地址: 我就是那个地址