zoukankan      html  css  js  c++  java
  • 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里面创建一个你原本想继承的那个类的实例作为一个“中介”,去实现具体的其它功能。

  • 相关阅读:
    python多版本切换
    python之禅
    Python int与string之间的转化
    pycharm工具使用
    python学习路线图
    traceback.print_exc()的用法
    他人学习Python感悟
    【西北师大-19软工】第十三、十四次作业汇总暨期末总结
    【西北师大-19软工】第十二次作业成绩汇总
    第十七周助教工作总结——NWNU李泓毅
  • 原文地址:https://www.cnblogs.com/Jam01/p/3633566.html
Copyright © 2011-2022 走看看