在了解Observer Pattern 之前,先看看一个天气监控系统的设计:
在这个系统中包含三个部分:Weather Station(通过传感器实时获取数据),WeatherData 对象(跟踪Weather Station传输过来的 数据并且更新显示界面),Display device(显示当前有关天气数据)。
我们现在所要做的是设计一个app,通过利用WeatherData对象来实时显示天气数据:包括当前状况(温度,湿度和气压)、天气数据、一个简单的天气预报。
以下是WeatherData对象的大致设计:
现在第一步要做的是实现measurementsChanged()方法,通过它来更新当前状况、天气数据、天气预报信息。(这里切记有三个显示元素)。在实现这个系统的时候同时还要考虑到可扩展性,将来我们可能对显示要素进行不断扩展或删除。
以下是对实现measurementsChanged()方法进行的第一次尝试:
public class WeatherData {
//instance variable declarations
public void measurementsChanged(){
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp,humidity,pressure);//更新第一个显示要素
statisticsDisplay.update(temp,humidity,pressure);//更新第二个显示要素
forecastDisplay.update(temp,humidity,pressure);//更新第三个显示要素
//other WeatherData methods here
}
}
按照以上的实现的方法,如果我想再增加一个显示元素,那么我不得不对上面的代码进行修改,所以很难维护。
对于这种问题我们可以利用Observer Pattern,在现实生活中我们经常可以看到Observer Pattern的影子,比如对报纸的订阅,假设某报纸被三个用户甲、乙、丙订阅,那么每当有新版本的报纸发布,他们每人都会得到一份,如果有一天甲用户打电话给出版商取消订阅,那么他就不会继续得到这种服务,如果某一天用户丁觉得这报纸不错,决定订阅,那么他就会收到新版的报纸。
Observer Pattern的定义:定义了对象之间一对多的相互依赖关系,所以当一个对象(Subject)改变状态的时候,所有依赖这个对象的对象(Observers)将会收到通知并做相应的更新。
Observer Pattern的类图定义:
从上图看出(1)只要对象实现了Observer接口,就可以成为Subject中observers变量中的一员。(2)Subject可以随时增加或去除observer对象。(3)任何普通的对象都能够成为observer,只要实现Observer接口,并不需要对对象本身做任何改变。(4)我们可以将Subject和observer对象用作别的用途,因为他们之间的关系不太紧密(低耦合),增加了代码重用性。(5)我们可以随意改动Subject和observer对象,因为他们之间互不影响(低耦合),只要他们实现Subject接口或Observer接口。
在这里引入另一个设计准则:在设计相互交互的对象时,尽量使这些对象之间处于低耦合的状态。
我们现在可以将上面的设计方法用于对天气监控系统的设计,将WeatherData对象视为Subject,将各种显示元素视为Observer:
下面实现以上设计:
1.接口设计:
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);
}
public interface DisplayElement {
public void display();
}
2.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(){
for(int i = 0;i < observers.size();i++){
Observer observer = (Observer)observers.get(i);
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 class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature +
"F degrees and " + humidity + "% humidity");
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
}
4.测试代码:
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);//Current conditions: 80.0F degrees and 65.0% humidity
}
}