设计模式学习--观察者模式(Oberser Pattern)

来源:互联网 发布:mwc飞控源码 编辑:程序博客网 时间:2024/06/06 19:59

设计模式学习–观察者模式(Oberser Pattern)

什么是观察者模式?
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

怎么设计一个观察者模式的程序?
确定两个对象:1. 主题
2.观察者

确定这两个对象之间的关系:
主题对象管理某些数据,一旦数据发生改变,会主动向观察者进行通知,然而观察者不必向主题进行索取。
主题并不知道具体的观察者是谁,这是它们之间的关系。

以上涉及到的设计原则:
为了交互对象之间的松耦合设计而努力

具体实例:气象站的实现

  1. 定义一个主题接口Subject
[java] view plaincopypackage observerPattern;  /**  * 主题接口  */  public interface Subject {      public void registerObserver(Observer o);   //这两个方法都需要一个观察者作为变量,该观察者是用那个来注册和删除的      public void removeObserver(Observer o);      public void notifyObserver();               //当主题状态发生改变时,这个方法会被调用,以通知所有的观察者  }  

2. 定义一个观察者接口Observer

[java] view plaincopypackage observerPattern;  /**  * 观察者接口  */  public interface Observer {      public void update(float temp, float humidity, float pressure);  }  

3. 定义一般气象布告板接口DisplayElement

[java] view plaincopypackage observerPattern;  /**  * 公告板接口  */  public interface DisplayElement {      public void display();  }     

4. 定义主题类:WeatherData实现接口

[java] view plaincopypackage observerPattern;  import java.util.ArrayList;  /**  * WeatherData实现了Subject接口  */  public class WeatherData implements Subject {      private ArrayList observers;    //用于记录观察者      private float temperature;      //温度      private float humidity;         //湿度      private float pressure;         //压力      public WeatherData() {          observers = new ArrayList();      }      @Override      public void registerObserver(Observer o) {          observers.add(o);      }      @Override      public void removeObserver(Observer o) {          int i = observers.indexOf(o);          if(i >= 0) {              observers.remove(i);          }      }      @Override      public void notifyObserver() {          for(int i = 0; i < observers.size(); i++) {              Observer observer = (Observer)observers.get(i);              observer.update(temperature, humidity, pressure);          }      }      public void measurementsChanged() {          notifyObserver();      }      public void setMeasurements(float temperature, float humidity, float pressure) {          this.temperature = temperature;          this.humidity = humidity;          this.pressure = pressure;          measurementsChanged();      }  }  

5. 定义四个布告板类实现观察者接口和布告板接口

[java] view plaincopypackage observerPattern;  /**  * 观察者类实现观察者接口和显示板接口  */  public class CurrentConditionDisplay implements Observer, DisplayElement {      private float temperature;      private float humidity;      private Subject weathderData;      public CurrentConditionDisplay(Subject weathderData) {          this.weathderData = weathderData;          weathderData.registerObserver(this);        //注册      }      @Override      public void display() {          System.out.println("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");      }      @Override      public void update(float temp, float humidity, float pressure) {          this.temperature = temp;          this.humidity = humidity;          display();      }  }  [java] view plaincopypackage observerPattern;  /**  * 天气统计布告板  */  public class StatisticsDisplay implements Observer, DisplayElement {      private float maxTemp = 0.0f;;  //最大温度      private float minTemp = 200;    //最小温度      private float tempSum = 0.0f;   //统计温度和      private int numReadings;        //统计温度次数      private WeatherData weatherData;      public StatisticsDisplay(WeatherData weatherData) {          this.weatherData = weatherData;          weatherData.registerObserver(this);      }      @Override      public void display() {          System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);      }      @Override      public void update(float temp, float humidity, float pressure) {          tempSum += temp;          numReadings++;          if(temp > maxTemp) {              maxTemp = temp;          }          if(temp < minTemp) {              minTemp = temp;          }          display();      }  }  [java] view plaincopypackage observerPattern;  /**  * 天气预报布告板  */  public class ForecastDisplay implements Observer, DisplayElement {      private float currentPressure = 29.92f; //当前气压      private float lastPressure;             //以往气压      private WeatherData weatherData;      public ForecastDisplay(WeatherData weatherData) {          this.weatherData = weatherData;          weatherData.registerObserver(this);      }      @Override      public void display() {          System.out.println("Forcast:");          if(currentPressure > lastPressure) {              System.out.println("Improving weather on the way!");          } else if(currentPressure == lastPressure) {              System.out.println("more of the same");          } else if(currentPressure < lastPressure) {              System.out.println("Watch out for cooler, rainy weather");          }      }      @Override      public void update(float temp, float humidity, float pressure) {          lastPressure = currentPressure;          currentPressure = pressure;          display();      }  }  [java] view plaincopypackage observerPattern;  /**  * 酷热指数布告板  * 注:那个计算酷热指数的公式不必深究  */  public class HeatIndexDisplay implements Observer, DisplayElement {      float heatIndex = 0.0f;      private WeatherData weatherData;      public HeatIndexDisplay(WeatherData weatherData) {          this.weatherData = weatherData;          weatherData.registerObserver(this);      }      public void update(float t, float rh, float pressure) {          heatIndex = computeHeatIndex(t, rh);          display();      }      private float computeHeatIndex(float t, float rh) {          float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh)               + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh))               + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +              (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 *               (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) +               (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +              0.000000000843296 * (t * t * rh * rh * rh)) -              (0.0000000000481975 * (t * t * t * rh * rh * rh)));          return index;      }      public void display() {          System.out.println("Heat index is " + heatIndex);      }  }  

6. 来吧,开始测试

[java] view plaincopypackage observerPattern;  /**  * 测试类 *  */  public class WeatherStation {      public static void main(String[] args) {          //建立一个WeatherData对象          WeatherData weatherData = new WeatherData();          //第一个布告板          CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(                  weatherData);          StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);          ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);          HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);          // 模拟新的气象数据          weatherData.setMeasurements(80, 65, 30.4f);          weatherData.setMeasurements(82, 70, 29.2f);          weatherData.setMeasurements(78, 90, 29.2f);      }  }  

7. 测试结果:
[plain] view plaincopy
Current coditions: 80.0F degress and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forcast:
Improving weather on the way!
Heat index is 82.95535
Current coditions: 82.0F degress and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forcast:
Watch out for cooler, rainy weather
Heat index is 86.90124
Current coditions: 78.0F degress and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forcast:
more of the same
Heat index is 83.64967

以上的观察者模式实现是通过主题以“推”的方式通知观察者们,观察者可以在一次通知中一口气得到所有东西。

因为观察者与主题发生了争吵,观察者有自己的想法,希望能“拉”走主题的状态,然而Java内置的Observer模式就支持这样,下面来看看吧。

1. 继承Observable类的WeatherData(不再需要自定义接口了,但这样真的好吗?)
[java] view plaincopy
package weatherObservable;

import java.util.Observable;
/**
* 使用Java内置的观察者模式
*/
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;

/**  * 我们的构造器不再需要为了记住观察者们而建立数据结构了  */  public WeatherData(){}  public void measurementsChanged() {      setChanged();       //Observable类方法      notifyObservers();  }  public void setMeasurements(float temperature, float humidity, float pressure) {      this.temperature = temperature;      this.humidity = humidity;      this.pressure = pressure;      measurementsChanged();  }  public float getTemperature() {      return temperature;  }  public float getHumidity() {      return humidity;  }  public float getPressure() {      return pressure;  }  

}

2. 4个布告板中的代码稍微发生了点变化

[java] view plaincopypackage weatherObservable;  import java.util.Observable;  import java.util.Observer;  /**  * 实现Java内置的观察者接口,布告板不变  */  public class CurrentConditionDisplay implements Observer, DisplayElement{      Observable observable;      private float temperature;      private float humidity;      public CurrentConditionDisplay(Observable observable) {          this.observable = observable;          observable.addObserver(this);       //登记为观察者      }      @Override      public void display() {          System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");      }      /**      * 在这个方法当中,先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和温度测量值,最后调用display();      */      @Override      public void update(Observable obs, Object arg) {          if(obs instanceof WeatherData) {              WeatherData weatherData = (WeatherData) obs;              this.temperature = weatherData.getTemperature();              this.humidity = weatherData.getHumidity();              display();          }      }  }  [java] view plaincopypackage weatherObservable;  import java.util.Observable;  import java.util.Observer;  /**  * 天气预报布告板  */  public class ForecastDisplay implements Observer, DisplayElement {      private Observable observable;      private float currentPressure = 29.92f; //当前气压      private float lastPressure;             //以往气压      public ForecastDisplay(Observable observable) {          this.observable = observable;          observable.addObserver(this);      }      @Override      public void display() {          System.out.println("Forcast:");          if(currentPressure > lastPressure) {              System.out.println("Improving weather on the way!");          } else if(currentPressure == lastPressure) {              System.out.println("more of the same");          } else if(currentPressure < lastPressure) {              System.out.println("Watch out for cooler, rainy weather");          }      }      @Override      public void update(Observable o, Object arg) {          if (o instanceof WeatherData) {              WeatherData weatherData = (WeatherData)observable;              lastPressure = currentPressure;              currentPressure = weatherData.getPressure();              display();          }      }  }  [java] view plaincopypackage weatherObservable;  import java.util.Observable;  import java.util.Observer;  /**  * 天气统计布告板  */  public class StatisticsDisplay implements Observer, DisplayElement {      private float maxTemp = 0.0f;;  //最大温度      private float minTemp = 200;    //最小温度      private float tempSum = 0.0f;   //统计温度和      private int numReadings;        //统计温度次数      private Observable observable;      public StatisticsDisplay(Observable observable) {          this.observable = observable;          observable.addObserver(this);      }      @Override      public void display() {          System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);      }      @Override      public void update(Observable obs, Object arg) {          if(obs instanceof WeatherData) {              WeatherData weatherData = (WeatherData)obs;              float temp = weatherData.getTemperature();              tempSum += temp;              numReadings++;              if (temp > maxTemp) {                  maxTemp = temp;              }              if (temp < minTemp) {                  minTemp = temp;              }              display();          }      }  }  [java] view plaincopypackage weatherObservable;  import java.util.Observable;  import java.util.Observer;  /**  * 酷热指数布告板  * 注:那个计算酷热指数的公式不必深究  */  public class HeatIndexDisplay implements Observer, DisplayElement {      float heatIndex = 0.0f;      private WeatherData weatherData;      private Observable observable;      public HeatIndexDisplay(Observable observable) {          this.observable = observable;          observable.addObserver(this);      }      private float computeHeatIndex(float t, float rh) {          float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh)               + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh))               + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +              (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 *               (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) +               (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +              0.000000000843296 * (t * t * rh * rh * rh)) -              (0.0000000000481975 * (t * t * t * rh * rh * rh)));          return index;      }      public void display() {          System.out.println("Heat index is " + heatIndex);      }      @Override      public void update(Observable obs, Object arg) {          if(obs instanceof WeatherData) {              WeatherData weatherData = (WeatherData)observable;              float t = weatherData.getTemperature();              float rh = weatherData.getHumidity();              heatIndex = computeHeatIndex(t, rh);          }          display();      }  }  

3. 测试类不变

[java] view plaincopypackage weatherObservable;  /**  * 测试类  */  public class WeatherStation {      public static void main(String[] args) {          //建立一个WeatherData对象          WeatherData weatherData = new WeatherData();          //第一个布告板          CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(                  weatherData);          StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);          ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);          HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);          // 模拟新的气象数据          weatherData.setMeasurements(80, 65, 30.4f);          weatherData.setMeasurements(82, 70, 29.2f);          weatherData.setMeasurements(78, 90, 29.2f);      }  }  

4. 但测试结果发生了变化:观察者被通知的次序发生了变化
[plain] view plaincopy
Heat index is 82.95535
Forcast:
Improving weather on the way!
Avg/Max/Min temperature = 80.0/80.0/80.0
Current conditions: 80.0F degrees and 65.0% humidity
Heat index is 86.90124
Forcast:
Watch out for cooler, rainy weather
Avg/Max/Min temperature = 81.0/82.0/80.0
Current conditions: 82.0F degrees and 70.0% humidity
Heat index is 83.64967
Forcast:
more of the same
Avg/Max/Min temperature = 80.0/82.0/78.0
Current conditions: 78.0F degrees and 90.0% humidity

以上的实现被认为不是那么“正确”的,为什么呢?
有以下原因:
1. Observable是一个“类”,而不是一个接口,也没有实现一个接口,限制了它的使用和复用。
2. Observable将关键的方法保护起来了,违反了 “多用组合,少用继承”的设计原则。

当然在JDK中不只有这个地方用到了观察者模式,比如以下几个地方也用到了:
1. Swing
2. JavaBeans
3. RMI

0 0
原创粉丝点击