一、认识观察者模式
类似于报纸订阅的过程:
- 报社的任务就是出版报纸;
- 向报社订阅报纸后,只要有新报纸出版,就会给客户送来。只要是客户,就会一直收到新报纸;
- 当客户不想看报纸的时候,取消订阅,报社就不会送报纸过来;
- 只要报社还在运营,就会有人向他们订阅报纸或取消订阅。
将出版者改称为主题(Subject),订阅者称为观察者(Observer),则观察者模式具有以下特点:
- 主题对象管理某些数据,当主题内的某些数据改变,就会通知观察者。一旦数据改变,新的数据会以某种形式送到观察者手上;
- 观察者已经订阅主题,在主题数据改变时能收到更新;
- 不是观察者的对象不会被通知。
观察者模式的定义:
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
二、观察者模型的设计:
- Subject接口,包含注册、移除、通知方法;
- Observer接口,包含更新方法;
- 一个实现了Subject接口的类;
- 多个实现了Observer接口的类。
实现一个观察者模式(一个气象观测的例子):
Subject接口实现:
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
Observer接口设计:
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
可以设计一个Display接口,提供展示方法:
public interface 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(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();
}
}
设计两个观察者,实现Observer接口,一个保存现在的天气信息,一个保存天气信息的最大、最小和平均值:
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 StatisticsDisplay implements Observer, DisplayElement {
private ArrayList<Float> temperatures;
private ArrayList<Float> humiditys;
private Subject weatherData;
public StatisticsDisplay(Subject weatherData){
this.weatherData = weatherData;
this.temperatures = new ArrayList<>();
this.humiditys = new ArrayList<>();
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure){
this.temperatures.add(temperature);
this.humiditys.add(humidity);
display();
}
public void display(){
Collections.sort(temperatures);
Collections.sort(humiditys);
Float maxTemperature = temperatures.get(temperatures.size()-1);
Float minTemperature = temperatures.get(0);
Float maxHumidity = humiditys.get(humiditys.size()-1);
Float minHumidity = humiditys.get(0);
Float sumTemp = 0.0f;
Float sumHum = 0.0f;
for (Float f : temperatures)
sumTemp += f;
for (Float f : humiditys)
sumHum += f;
Float aveTemp = sumTemp/temperatures.size();
Float aveHum = sumHum/humiditys.size();
System.out.println("Max temperature : "+maxTemperature +"F degrees and max Humidity:"+maxHumidity+
"% humidity\nMin temperature : "+minTemperature+"F degrees and min humidity : "+
minHumidity+"% humidity\nAverage Temperature : "+aveTemp+"F degrees and average humidity : "+aveHum+"% humidity");
}
}
测试代码如下:
public class Weathertation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(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
Max temperature : 80.0F degrees and max Humidity:65.0% humidity
Min temperature : 80.0F degrees and min humidity : 65.0% humidity
Average Temperature : 80.0F degrees and average humidity : 65.0% humidity
Current conditions: 82.0F degrees and 70.0% humidity
Max temperature : 82.0F degrees and max Humidity:70.0% humidity
Min temperature : 80.0F degrees and min humidity : 65.0% humidity
Average Temperature : 81.0F degrees and average humidity : 67.5% humidity
Current conditions: 78.0F degrees and 90.0% humidity
Max temperature : 82.0F degrees and max Humidity:90.0% humidity
Min temperature : 78.0F degrees and min humidity : 65.0% humidity
Average Temperature : 80.0F degrees and average humidity : 75.0% humidity
三、Java内置观察者模式
Java 内置了观察者模式的实现,java.util包中包含了Observer接口和Observable类。
Subject类:
public class Weather extends Observable {
private float temperature;
private float humidity;
private float pressure;
public Weather(){}
public void measurementsChanged(){
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public float getTemperature() {
return temperature;
}
}
Observer类:
public class CurrentConditions implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditions(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg){
if(obs instanceof Weather){
Weather weather = (Weather) obs;
this.temperature = weather.getTemperature();
this.humidity = weather.getHumidity();
display();
}
}
public void display(){
System.out.println("Current conditions: "+temperature+"F degrees and "+ humidity+"% humidity" );
}
}
- 不需要添加、删除、通知方法,在Observable超类中已实现了这些方法;
- 构造器不需要建立数据结构来保存数据;
- 发出的通知顺序不同。
- Observable是一个类,限制了它的复用。
小结
- 观察者模式被大量的用在Swing和许多GUI框架上;
- 此模式也被用在JavaBeans、RMI方面。