本章目录如下:
一、阶段一
二、阶段二
三、设计原则总结
本章需求是设计一个气象展示应用,并通过代码迭代、优化过程得出前人的设计经验之一观察者模式。下面以代码的迭代演化过程为线索介绍。
一、阶段一
需求:气象监测站希望创建一个气象展示应用,默认有三个布告板,且允许第三方接入/离开。监测站提供WeatherData类,在气象数据变化时气象站调用WeatherData对象的measurementsChanged方法来更新气象数据。
需求分析:我们只需要在WeatherData类的measurementsChanged方法中获取气象数据,并更新我们的气象板即可。
代码实现如下:
public class WeatherData{
//实例变量声明,气象站已经实现
public void measurementsChanged() {
//首先,调用 WeatherData 的三个getXxx()方法,以取得最近的测量值。这些getXxx()方法气象站已经实现好了。
float temp = getTemperature() ;
float humidity = getHumidity() ;
float pressure = getPressure() ;
//其次,更新布告板
currentConditionsDisplay . update (temp, humidity, pressure) ;
statisticsDisplay .update (temp, humidity, pressure) ;
forecastDisplay .update (temp, humidity, pressure) ;
}
//这里是其他WeatherData方法,,气象站已经实现
}
该实现的缺点:面对需求,首先要找出变化,将变化分离出来,然后在依据设计原则挑选合适的设计模式。本章需求的变化之处是允许第三方接入,那么我们就需要将“允许第三方接入”功能分离出来,如果像阶段一的代码一样,不分离的话会使代码不能扩展和维护。下面我们进入第二阶段:优化。
二、阶段二
从报纸订阅的角度来讲,观察者模式=出版者+订阅者;从理论定义角度来讲,观察者模式=主题(Subject)/可观察者(Observable)+观察者(Observer)。定义:观察者模式定义了对象之间的一对多依赖, 这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式类图如下:
为什么要在主题实现类中加入一些get方法?因为主题发送的信息可能有很多是观察者不需要的数据,主题实现类中加入get方法可以使观察者接收到主题对象推送时主动去拉去数据。
类图解释说明:
(1)、主期只知道观察者实现了某个核口(也就是Observer接口) ,主题不需要知道观察者的具体类是谁,做了些什么或其他任何细节。
(2)、任何时候我们都可以增加新的观察者,因为主题唯依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。
(3)、有新类型的观察者出现时,主题的代码不需要修改。
(4)、因为两者是松糊合的,改变主题或观察者其中一方,并不会影响另一方,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。所以可以独立地复用主题或观察者。
松耦合的威力:当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。松糊合的设计之所以能让我们建立有弹性的00系统,能够应对变化,是因为对象之间的互相依赖降到了最低。观察者模式让主题和观察者之间松耦合。
设计原则3:为了交互对象之间的松耦合设计而努力。
根据以上对观察者模式的说明和新的设计原则的了解,我们将气象站的新类图设计如下:
气象站代码如下:
public interface Subject {//Subject接口
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
public interface Observer {//Observer接口
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {//DisplayElement接口
public void display();
}
=============================气象站subject实现类======================
public class WeatherData implements Subject {
private ArrayList
observers; private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList
(); }
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() {
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();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
==========================当前状况布告板============================
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 WeatherStation {
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);
}
}
Java API内置了观察者模式,即java.util包 (package) 中的Observable类与Observer接口。Observable类中实现了addObserver(Observer o)、deleteObserver(Observer o)、notifyObservers()、setChanged()方法,主题实现类继承该类即可使用这些方法。需要注意两点:
(1)、使用前需要导入import java.util.Observable和import java.util.Observer包
(2)、调用notifyObservers()之前需要先调用setChanged()来指示状态已经改变。
三、设计原则总结
设计原则1:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化 T个T的代码混在一起。该设计原则作用: “把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分”。
设计原则2:针对接口编程,而不是针对实现编程。===我的理解是这个原则的使用优先级排在“设计原则1:变化原则”之后,即该原则是一个在大模式已确定需要实现具体类时针对实现类采用的原则,针对范围比较小。
设计原则3:为了交互对象之间的松耦合设计而努力。