有这么一个需求。
- 气象站会发布气象监测数据,包括(温度,湿度,气压)
- 需要开发一个应用,从气象站取得数据,并更新应用的布告板。
- 布告板有三类,分别是显示当前天气情况,气象统计数据,天气预报。
气象站提供的数据结构如下,很简单,就是温度,湿度,气压的获取,以及气象数据变化时触发的方法。
public class WeatherData {
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
//气象数据发生改变的时候执行的方法
public void measurementsChanged() {
}
}
按照通常面向过程的编程方式,会是在 measurementChanged 方法当中添加布告板的显示更新逻辑。
public class WeatherData {
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(); //当前天气情况显示
ForecastDisplay forecastDisplay = new ForecastDisplay(); //天气预报显示
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(); //气象统计显示
public void measurementsChanged() {
currentConditionDisplay.update(temperature, humidity, pressure);
forecastDisplay.update(temperature, humidity, pressure);
statisticsDisplay.update(temperature, humidity, pressure);
}
}
这样的编程方式导致每次要添加布告板的实现都要来修改 WeatherData 的代码。
引入观察者模式去解决这个问题。
什么是观察者模式,可以通过报纸和杂志的订阅来解释。
- 报社的任务就是出版报纸
- 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
- 当你不想看报纸的时候,取消订阅,他们就不会给你送新报纸来。
- 只要报社还在运营,就会有人向他们订阅报纸或者曲线订阅
观察者模式将出版者称为 “主题(Subject)”,订阅者称为“观察者(Observer)”
同订阅报纸的模式。
- 观察者可以订阅主题
- 主题的信息发生改变时,会通知观察者,把数据传递给观察者。
- 观察者不想接受主题的信息的时候就可以取消主题。
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖都会收到通知并且自动更新。
观察者类图如下:

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。
改变主题或者观察者的其中一方,并不会影响另一方。因为两者时松耦合的,所以只要他们直接的接口仍被遵守,我们就可以自由的改变他们。
松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
根据观察者模式,重写气象布告板的程序。
类图设计如下:

代码程序如下:
接口的设计
//主题定义
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
//观察者接口定义
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
//布告板接口定义
public interface DisplayElement {
public void display();
}
气象站的数据源程序
public class WeatherData implements Subject{
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public WeatherData() {
observers = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
@Override
public void notifyObserver() {
float temp = this.getTemperature();
float humidity = this.getHumidity();
float pressure = this.getPressure();
for (Observer o : observers) {
o.update(temp, humidity, pressure);
}
}
public void measurementsChanged() {
this.notifyObserver();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
this.measurementsChanged();
}
}
布告板的实现
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject subject;
public CurrentConditionDisplay(Subject weatherData) {
this.subject = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
this.display();
}
@Override
public void display() {
System.out.println("this is currentConditionDisplay. temp: "+this.temperature+", humidity: "+this.humidity+", pressure:"+this.pressure);
}
}
测试程序
public class Main {
public static void main(String[] args) {
WeatherData s = new WeatherData();
Observer o = new CurrentConditionDisplay(s);
s.setMeasurements(1, 2, 3);
s.setMeasurements(4, 5, 6);
}
}
测试结果:
this is currentConditionDisplay. temp: 1.0, humidity: 2.0, pressure:3.0
this is currentConditionDisplay. temp: 4.0, humidity: 5.0, pressure:6.0
由于观察者模式的普遍,各类语言或者框架都提供了观察者模式的实现。
例如 java 提供了 Observable 和 Observer 两个超类和接口。分别对应于前文提及的主题和观察者。
使用 Observable 和 Observer 实现布告板程序
气象站数据源程序,继承于 Observable 超类
package observer.example3;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
public class WeatherData extends Observable{
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public WeatherData() {
}
public void measurementsChanged() {
this.setChanged(); //标识主题改变
this.notifyObservers(this); //通知所有的观察者
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
this.measurementsChanged();
}
}
布告板程序,实现 Observer 接口,必须实现 update 方法
package observer.example3;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Observable subject;
public CurrentConditionDisplay(Observable weatherData) {
this.subject = weatherData;
weatherData.addObserver(this);
}
@Override
public void display() {
System.out.println("this is currentConditionDisplay. temp: "+this.temperature+", humidity: "+this.humidity+", pressure:"+this.pressure);
}
//响应主题的推送
@Override
public void update(Observable o, Object arg) {
WeatherData w = (WeatherData)o;
this.temperature = w.getTemperature();
this.humidity = w.getHumidity();
this.pressure = w.getPressure();
this.display();
}
}
测试程序
public class Main {
public static void main(String[] args) {
WeatherData s = new WeatherData();
Observer o = new CurrentConditionDisplay(s);
s.setMeasurements(1, 2, 3);
s.setMeasurements(4, 5, 6);
}
}
测试程序
this is currentConditionDisplay. temp: 1.0, humidity: 2.0, pressure:3.0
this is currentConditionDisplay. temp: 4.0, humidity: 5.0, pressure:6.0
本文介绍如何使用观察者模式实现气象站数据更新布告板。通过解耦气象站与布告板,实现数据更新时自动通知并更新多个布告板,避免代码冗余,提高系统灵活性。
1284

被折叠的 条评论
为什么被折叠?



