生活中的观察者模式
想象一个报纸订阅情景:
- 报社的业务就是出版报纸,向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来。只要你是他们的用户,你就会一直收到新的报纸
- 当你不再需要看报的时候,取消订阅,他们就不会再送新报纸了
- 同时,只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅报纸
观察者模式UML(类图)
观察者模式每个主题可以有多个观察者,对象使用主题中的registerObserver()注册为观察者,当主题状态改变时,notifyObservers()去更新所有当前观察者,观察者接口中的update()被调用。这样两个对象之间松耦合,依然可以交互,但不太清楚彼此的细节:
- 主题只知道观察者实现了某个接口,不需要知道观察者的具体类是谁
- 任何时候都可以增加删除观察者,主题不会受到任何影响,不需要为了支持新的类型而修改主题代码,只需在新的类里实现观察者接口,并注册为观察者即可
- 可独立复用主题或者观察者
观察者模式实战
我们假设现在有个需求,是建立一个气象观测站。我们已经能从某天气数据中心拿到一个weatherData对象,它可以负责追踪目前的天气状况,我们需要建立三个相应的dashboard:当前数据,统计数据,和预测数据,来显示不同的气象数据。当weatherData获得最新的测量数据时,dashboard实时更新。
ok,那我们来尝试画一下这个应用的UML图:
talk is cheap, show me the code:
定义接口
public interface Subject {
void regitsterObserver(Observer o);
void removeObserver(Observer o);
void notifyObserver();
}
interface Observer {
void update(float temp, float humidity, float pressure);
}
interface DisplayElement {
void display();
}
weatherData实体类
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void regitsterObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(o);
}
}
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
observers.get(i).update(temperature, humidity, pressure);
}
}
public void measurementChanged() {
notifyObservers();
}
public void setMeasurement(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
}
建立三个dashboard,为了举例子,我们先只建currentCondition dashboard
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.regitsterObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
@Override
public void display() {
System.out.println("Current weather condition: " + temperature
+ "F degrees, and " + humidity + "% humidity, and " + pressure + "pa");
}
}
Cool! 现在我们可以写测试程序来看一下结果啦!
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay conditionDisplay = new CurrentConditionDisplay(weatherData);
weatherData.setMeasurement(80, 65, 30.5f);
}
}
//输出: Current weather condition: 80.0F degrees, and 65.0% humidity, and 30.5pa
以上,当我们需要加入新的或者删除observer时,只需要加入/删除相应实现了Observer接口的类就可以了,并不需要对Subject做任何改动。
Java内置的观察者模式
以上的例子中,主题每次更新数据时候,需要push到每个订阅了它的观察者,这样主题就是有状态的了,它需要知道观察者怎么去修改数据,是否可以提供一种pull的形式,主题只需要提供一些getter方法,这样由观察者去自己做出对所需数据的修改,而当主题增加新的状态类型的时候,它只需要相应增加getter方法就可以了。
在Java中,java.util.Observable就类似与我们上面的Subject,而java.util.Obsever就是观察者。Observable内置了addObsever(), deleteObsever(), notifyObserver(), setChanged()等方法,conreteClass可以去继承,而Observer接口里只有一个update(Observable o, Object arg)方法,如果你想push,你可以调用Observable里的notifyObservers(args)方法,否则,观察者必须从可观察对象中pull,让我们调整上面代码的例子,来使用Java内置的观察者模式:
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
}
public void measurementChanged() {
setChanged();//只有把changed flag设为true,notifyObservers()才会通知
notifyObservers();
}
public void setMeasurement(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
public class CurrentConditionDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
private float pressure;
public CurrentConditionDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
this.pressure = weatherData.getPressure();
display();
}
}
@Override
public void display() {
System.out.println("Current weather condition: " + temperature
+ "F degrees, and " + humidity + "% humidity, and " + pressure + "pa");
}
}
一些思考
我们可以发现,java.util.Observable是一个类,而不是接口,你必须设计一个类去继承它,如果这个类想同时拥有另一个超类的功能,那么就会陷入两难,因为Java不支持多继承 ,而你又必须继承Observable来创建实例并才能调用setChanged()方法,这又违反了“favor composition over inheritance”的原则。所以,如果你能够扩展Observable,那么它可能会符合你的要求,否则,你可能需要像开头一样,自己实现一套观察者模式了。
之后,我们会看到观察者模式的代表人物:MVC
网友评论