Java设计模式之观察者模式
摘要:观察者模式(ObserverPattern)。用于实时监测某些Object的动态、只要Object一改变、那么他的所有观察者Observer都会知道、之后观察者会根据Object的改变进行下一步操作。这个在SWING编程中是最常见的。那些Listener就是观察者。
一:问题的引出
要实现一个天气预报的功能、当天气的数据发生变化的时候、会实时的以三种形式来显示天气:当前天气情况、统计分析情况、天气预报。有可能还会有第四种显示。
这里我们不管数据是如何来的、就假设我们已经能获取到数据了。在程序中是通过调用被观察的对象的notifyObserver方法来通知所有Observer来实现的。
二:问题分析
1、通过分析我们得出一个简单的总结:就是天气数据一旦有更新、那么就要实时的改变显示。观察者模式可以很好的解决这种模型的问题。
2、既然确定要使用观察者模型、就要定位出谁是观察者、谁是被观察者、这里有个简单的原则、观察者与被观察者之间的关系是多对一的关系、也就是说被观察者只有一个、那么就是我们的天气数据信息、观察者则是三种要显示不同信息的终端(当然也可以有第四个观察者)。
3、因为每个观察者都要动态的显示信息、所以我们应该抽象出来一个显示信息的类或者接口。
4、既然角色分工很清除了、接下来就是设计、与组装了。
5、考虑到可扩展性、低耦合、灵活性、和对扩展开放、对修改关闭的原则和面向接口编程、我们下面具体话类的设计。
6、根据角色我们可以抽象出三个接口:
a) 所有被观察者的接口――Subject;
b) 所有观察者的接口――Observer;
c) 显示信息的接口――DisplayElement;
7、对于Subject当然要拥有三个关于操作Observer的方法、注册、移除、和通知Observer的方法(观察者肯定要和被观察者结合起来)。
8、对于Observer肯定要有一个update方法、就是一旦检测到Subject有变动、就更新信息、所以还要实现DisplayElement接口。
二:具体实现
1、设计Subject接口:
package com.chy.dp.observer;
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
}
2、设计Observer接口:
package com.chy.dp.observer;
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
3、设计DisplayElement接口:
package com.chy.dp.observer;
public interface DisplayElement {
public void display();
}
4、设计具体的实现Subject接口的被观察者――WeatherDate
package com.chy.dp.observer;
import java.util.ArrayList;
import java.util.List;
public class WeatherDate implements Subject {
// Observer数组 可以简单想一下为什么不用LinkedList
private List
observers = new ArrayList
(); private float temperature; private float humidity; private float pressure; @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(observer); } } @Override public void notifyObserver() { for (int i = 0; i < observers.size(); i++) { Observer o = (Observer) observers.get(i); o.update(temperature, humidity, pressure); } } /** * 模仿数据变动时自动触发notifyObserver函数 */ public void measurementsChanged() { notifyObserver(); } /** * 模仿数据变动、即当我们调用这个方法时就说明数据有改变、 这样所有的观察者都会被通知 */ public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
5、接下来就是实现我们的观察者Observer的具体实现――CurrentConditionDisplay:
package com.chy.dp.observer;
public class CurrentConditionDisplay implements DisplayElement, Observer {
private float temperature;
private float humidity;
private Subject weatherDate;
/**
* 将被观察者通过构造方法传递进来、并将此观察者注册到被观察者中
* 这样观察者和被观察者就完美的结合了
* @param weatherDate
*/
public CurrentConditionDisplay(Subject weatherDate) {
super();
this.weatherDate = weatherDate;
this.weatherDate.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current comditions : " + temperature + " F degrees and " + humidity + "% humidity");
}
}
6、同样的、关于显示ForecastConditionDisplay、StatisticsDisplay代码与上面非常相似。
7、Client:
package com.chy.dp.observer;
@SuppressWarnings("unused")
public class Client {
public static void main(String[] args) {
WeatherDate subject = new WeatherDate();
ForecastConditionDisplay fcd = new ForecastConditionDisplay(subject);
CurrentConditionDisplay ccd = new CurrentConditionDisplay(subject);