10分钟一个设计模式系列-The Observer Pattern

10分钟一个设计模式系列

The Observer Pattern

1. 基础 Basic

    Observer Pattern,说穿了,类似于手机应用里面的“发布-订阅”的形式,有一个Subject接口提供内容的获取,更新,并且Subject需要注册(register)很多个Observer观察者,而具有显示数据功能Display的类将继承Observer接口作为观察者,只有当你在Subject里注册了这个Observer时,Subject一有数据更新就会通知(notify)所有注册的Observers去更新(update)内容。

现在我们来看看一个UML图,摘自《Head First Design Patterns》。

在这里面,我们可以看到有Subject接口和Observer接口,分别有两个ConcreteXX的具体实现接口功能的类。这就是Observer模式的基本概念。

 

2. 例子 Example

    我们来看一个具体的例子,有一个气象台WeatherStation负责获取天气的数据,天气气象台的类有3个getter,分别获取气温,湿度,气压,getTemperature(),getHumidity(),getPressure()。有很多个设备负责显示这些数据,那么应该如何实现这个例子?

Subject Interface:

1 public interface Subject {
2   public void registerObserver(Observer observer);
3   public void removeObserver(Observer observer);
4   public void notifyObservers(); 
5 }

Observer Interface:

1 public interface Observer {
2  public void update(float temp, float humidity, float pressure);
3 }

WeatherData Class:

 1 import java.util.Vector;
 2 
 3 public class WeatherData implements Subject{
 4     private float temperature;
 5     private float humidity;
 6     private float pressure;
 7     private Vector observers;
 8     public WeatherData() {
 9          
10     }
11 
12     @Override
13     public void registerObserver(Observer observer) {
14         // TODO Auto-generated method stub
15         observers.add(observer);
16     }
17 
18     @Override
19     public void removeObserver(Observer observer) {
20         // TODO Auto-generated method stub
21         int index = observers.indexOf(observer);
22         if (index >= 0)
23             observers.remove(index);
24     }
25 
26     @Override
27     public void notifyObservers() {
28         // TODO Auto-generated method stub
29         for (int i = 0; i < observers.size(); i++) {
30             Observer observer = (Observer)observers.get(i);
31             observer.update(temperature, humidity, pressure);
32         }
33     }
34     
35     public void measurementChanged() {
36         notifyObservers();
37     }
38     
39     public void setMeasurement(float temperature, float humidity, float pressure) {
40         this.temperature = temperature;
41         this.humidity = humidity;
42         this.pressure = pressure;
43         measurementChanged();
44     }
45 }

DisplayElement Interface(将动作抽象与实现分离):

1 public interface DisplayElement {
2   public void display();
3 }

ConditionDisplay Class:

 1 public class ConditionDisplay implements Observer,DisplayElement{
 2     private float temperature;
 3     private float humidity;
 4     private float pressure;
 5     private Subject weatherData;
 6     
 7     public ConditionDisplay(Subject weatherData) {
 8         this.weatherData = weatherData;
 9     }
10     
11     @Override
12     public void display() {
13         // TODO Auto-generated method stub
14         System.out.println("temperature: "+this.temperature);
15         System.out.println("humidity: "+this.humidity);
16         System.out.println("pressure: "+this.pressure);
17     }
18 
19     @Override
20     public void update(float temp, float humidity, float pressure) {
21         // TODO Auto-generated method stub
22         this.temperature = temp;
23         this.humidity = humidity;
24         this.pressure = pressure;
25         display();
26     }
27 
28 }

 

3. Java内置API实现 Java Built in Support API - Observable Class & Observer Interface

    其实,Java也有内置帮助实现Observer模式的功能,是java.util包里面的Observable类以及Observer接口。请注意,Observable是一个类,而Observer是一个接口,这两点很重要,稍后会详细解释。我们先看看Observable类。

其实,它就是一个类似的Subject接口的类,但这里面有一个setChanged()方法。当你的Subject数据更新时,你需要先setChanged(),再notifyObservers(),为什么会多出来这个setChanged()方法?暂且不表,我们先看一下notifyObservers()在java里是如何实现的。

 1 public void notifyObservers(Object arg) {
 2    Object[] arrLocal;
 3    synchronized (this) {
 4       if (!changed)
 5                  return;
 6       arrLocal = obs.toArray();
 7       clearChanged();
 8    }
 9    for (int i = arrLocal.length-1; i>=0; i--)
10        ((Observer)arrLocal[i]).update(this, arg);
11 }

从这段代码可以看到,它会有一个先判定boolean changed是否改变的过程。然后再一一调用Observer数组中的Observer.update()去更新各个Observer的内容。

那刚才的问题就可以解答了,为什么会多出来这个setChanged()方法?这个方法的好处是,你的subject不一定每时每刻都要去告诉observer观察者去更新内容,只有当想通知时,再通知。这就是好处了,但值得注意的是,如果去看java的源代码,setChanged()是protected,这也就会引起直接用java提供的类和接口实现这个模式会出现的一些不方便的地方。

Java中的Observer是一个接口,也是一个update()方法,所以没什么好说的。

4. Java Observable类的限制 Limitation of the Java Observable Class

    刚才说过,java中Observable是一个类,而且类中的setChanged()是一个protected方法。这就带来了一个问题,因为它是一个类,那么你只能作为一个子类subclass去继承它,但假如我们这个类需要继承别的具体的类呢,或许有人会说new一个Observable实例出来,但这里又有一个问题,它是setChanged()是protected方法,那么new出来的这个实例是无法调用setChanged()方法的,无法调用它,就没办法notifyObservers()。这里提供两种方法来解决这个问题:

1. 照旧继承你原来继承的另外的类,然后直接在这个子类里面重新实现Observable类的内容,或者重新写一个Subject接口,添加Observable的方法。

2. 继承Observable,然后在subclass里面创建一个你原本想继承的那个类的实例作为一个“中介”,去实现具体的其它功能。

posted on 2014-03-30 10:40  Jam_01  阅读(342)  评论(0编辑  收藏  举报

导航