观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
有这样一个需求:
一个气象站,它会提供气温,气压,湿度等气象数据,要实现一个显示当前气象状态的显示装置。
能显示当前气温、气压、湿度等。当气象站的数据一发生改变,就能通知显示装置。
使用观察者模式实现。
先看看观察者模式类图:
主题是一个接口,定义了注册观察者、删除观察者、通知方法
package com.headfirst.chapter2;
public interface Subject {//主题
public void registerObserver(Observer o);//添加观察者
public void removeObserver(Observer o);//删除观察者
public void notifyObserver();//通知观察者
}
观察者也是一个接口,主题的notifyObserver方法会调用观察者的update方法。
package com.headfirst.chapter2;
public interface Observer {//观察者
public void update(float temp,float humidity,float pressure);
}
根据上面的需求,我们实现具体的主题,代码如下:
package com.headfirst.chapter2;
import java.util.ArrayList;
import java.util.List;
public class WeatherDataSubject implements Subject {
private float temp;// 温度
private float humidity;// 温度
private float pressure;// 气压
private List observers;// 这个列表用于存入观察者
public WeatherDataSubject() {
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 notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(temp, humidity, pressure); //依次通知观察者
}
}
public void measurementChanged() {
notifyObserver();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
}
观察者
package com.headfirst.chapter2;
public class CurrentConditionDisplay implements Observer {
private float temp;
private float humidity;
private float pressure;
public CurrentConditionDisplay(Subject weatherDataSubject) {
weatherDataSubject.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {//显示方法
System.out.println("temp = "+temp+",humidity = "+humidity+", pressure = "+pressure);
}
}
测试类
package com.headfirst.chapter2;
public class Test {
public static void main(String[] args) {
WeatherDataSubject subject = new WeatherDataSubject();
Observer observer = new CurrentConditionDisplay(subject);
subject.setMeasurements(32, 11, 420);
}
}
使用JAVA内置的观察者模式
JAVA的util包中提供了内置的观察者模式API,分别是Observable类和Observer接口。
它可以使用推或拉的方式传输数据。
其中setChanged()方法用来标识状态是否已经改变的事实,如果调用notifyObservers()方法前没有先调用setChanged方法,那么观察都就不会被通知。
setChanged方法可以在更新观察者时,有更多弹性,可以适当通知观察者,例如气象站很敏锐,每十分之一度就会更新,这会造成显示装置不断被通知。
我们可以通过setChanged方法将通知频度设为一度通知更新一次。
把上面的例子使用内置观察者模式重写一遍:如下
package com.headfirst.chapter2;
import java.util.Observable;
//主题
public class WeatherData extends Observable {
private float temp;// 温度
private float humidity;// 温度
private float pressure;// 气压
public void measureChanged() {
setChanged();
notifyObservers();
}
public void setMeasurement(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measureChanged();
}
public float getTemp() {
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
package com.headfirst.chapter2;
import java.util.Observable;
import java.util.Observer;
//观察者
public class ConditionDisplay implements Observer {
private float temp;
private float humidity;
private float pressure;
public ConditionDisplay(Observable o) {
o.addObserver(this);
}
public void update(Observable arg0, Object arg1) {
if(arg0 instanceof WeatherData){
WeatherData data = (WeatherData)arg0;
this.temp = data.getTemp();
this.humidity = data.getHumidity();
this.pressure = data.getPressure();
display();
}
}
public void display(){
System.out.println("temp = "+temp+",humidity = "+humidity+", pressure = "+pressure);
}
}
测试类:
package com.headfirst.chapter2;
import java.util.Observer;
public class Test {
public static void main(String[] args) {
WeatherData data = new WeatherData();
Observer observer = new ConditionDisplay(data);
data.setMeasurement(23, 411, 89);
}
}