合工大很牛很牛牛

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            14 Posts :: 1 Stories :: 37 Comments :: 0 Trackbacks
           

          下面做一個(gè)天氣預(yù)報(bào)系統(tǒng),涉及到的參數(shù)一共有三種:temperature, humidity, pressure。外圍接受顯示裝置也分為三種:current condition display會(huì)列出當(dāng)前的氣溫,濕度和氣壓;statistics display會(huì)列出當(dāng)前的溫度最高,最低和平均值;forecast display列出將來預(yù)測(cè)的天氣狀況?,F(xiàn)在我們?cè)O(shè)想用一個(gè)WeatherDataObject這個(gè)對(duì)象從氣象臺(tái)實(shí)時(shí)接受數(shù)據(jù),再把更新的數(shù)據(jù)發(fā)送到外圍接受顯示裝置上去。如下:

          我們先看右邊的三個(gè)顯示設(shè)備,我們把它跟前一章的Duck做比較:Duck是一個(gè)總稱,被做成抽象類,下面有許多各種各樣的duck,被做成該抽象類的子類,Duck的各種會(huì)變化的功能比如quackableflyable被分別做成接口,每個(gè)接口下面有各種具體的功能實(shí)現(xiàn)它。同理,這個(gè)天氣情況顯示設(shè)備也好比是一個(gè)總稱,下面有三種顯示設(shè)備,而它的可變化的功能是display,因?yàn)槲磥砜赡茉僮龅谒姆N設(shè)備是用聲音報(bào)警的。。。

          因?yàn)槭菍?shí)時(shí)接受和顯示數(shù)據(jù),所以所有的設(shè)備必然包含update()功能,部分設(shè)備有display功能。UML如下:

          public interface Observer
          {

              public void update(float temperature, float humidity, float pressure);
          }

          public interface Display
          {

              public void display();
          }

          public class CurrentCondition implements Observer, Display
          {

              private float temperature;
              private float humidity;

              public void display()
              {
                  System.out.println("Current conditions: " + temperature + "F degrees and humidity " + humidity);
              }

              public void update(float temperature, float humidity, float pressure)
              {
                  this.temperature = temperature;
                  this.humidity = humidity;
                  display();
              }
          }

          public class Statistics implements Display, Observer
          {

              private static float maxT = 0;//歷史最高氣溫
              private static float minT = 100;//
          歷史最低氣溫
              private static float sumT = 0;//
          氣溫總和
              private static int num = 0;//
          測(cè)量次數(shù)
              public void display()
              {
                  System.out.println("Max Temperature:"+maxT);
                  System.out.println("Min Temperature:"+minT);
                  System.out.println("Average Temperature:"+sumT/num);
              }

              public void update(float temperature, float humidity, float pressure)
              {
                  if (temperature < minT)
                  {
                      minT = temperature;
                  }
                  if (temperature > maxT)
                  {
                      maxT = temperature;
                  }
                  sumT += temperature;
                  num++;
                  display();
              }
          }

          public class Forecast implements Display, Observer
          {

             private static float currentPressure = 29;//設(shè)定一個(gè)當(dāng)前氣壓的默認(rèn)值,為簡(jiǎn)化操作
              private static  float lastPressure = 29;//
          設(shè)定一個(gè)先前氣壓的默認(rèn)值

              public void display()
              {
                  System.out.println("Forecast:");
                  if (currentPressure > lastPressure) //
          氣壓升高,天氣轉(zhuǎn)暖;氣壓降低,天氣轉(zhuǎn)涼
                  {
                      System.out.println("will be warmmer.");
                  }
                  else if (currentPressure == lastPressure)
                  {
                      System.out.println("will be the same");
                  }
                  else
                  {
                      System.out.println("will be cooler");
                  }
              }

              public void update(float temperature, float humidity, float pressure)
              {
                  lastPressure = currentPressure;
                  currentPressure = pressure;
                  display();
              }
          }

           

          現(xiàn)在著重考慮WeatherData 這個(gè)對(duì)象怎么寫,該對(duì)象首先要能夠從氣象站收取氣溫,濕度,氣壓的信息(通過氣象臺(tái)使用setMeasurements方法來設(shè)定WeatherData中的各屬性的值),并向終端顯示設(shè)備更新氣象信息(使用upadeDisplay方法),所以有:

          public class WeatherData
          {
              private float temperature;
              private float humidity;
              private float pressure;

              public void updateDisplay()
              {
                  CurrentCondition currentCondition=new CurrentCondition();
                  Statistics statistics=new Statistics();
                  Forecast forecast=new Forecast();
                 
          currentCondition.update(temperature, humidity, pressure); //這部分是可變動(dòng)的,可是并沒有被分離出去
                  statistics.update(temperature, humidity, pressure);
                  forecast.update(temperature, humidity, pressure);

              }

              public void setMeasurements(float termperature, float humidity, float pressure)
              {
                  this.temperature=termperature;
                  this.humidity=humidity;
                  this.pressure=pressure;
                  updateDisplay();
              }
          }

           

          最后我們用個(gè)主函數(shù)來調(diào)試一下:

          public class Main
          {

              public static void main(String[] args)
              {
                  WeatherData wd=new WeatherData();
                  wd.setMeasurements(20, 10, 15);
                  wd.setMeasurements(21, 11, 16);
              }
          }

          輸出:

          Current conditions: 20.0F degrees and humidity 10.0
          Max Temperature:20.0
          Min Temperature:20.0
          Average Temperature:20.0
          Forecast:
          will be cooler
          Current conditions: 21.0F degrees and humidity 11.0
          Max Temperature:21.0
          Min Temperature:20.0
          Average Temperature:20.5
          Forecast:
          will be warmmer.

          可以看出,一切運(yùn)行正常,是不是這樣,該程序就搞定了呢?當(dāng)然不是!當(dāng)我們不想讓三個(gè)終端中的某一個(gè)接受天氣信息時(shí),或者我們要增加一個(gè)新的終端來顯示其他的一些天氣信息時(shí),我們勢(shì)必要改寫WeatherDate中的updateDisplay方法(上面源代碼中的紅字部分)。我們并沒有把WeatherDate中的不變化的setMeasurements方法和變化的updateDisplay方法分離開,也就是說沒有隔離出變化的部分,所以這個(gè)程序有待改進(jìn)。

          可不可以把updateDisplay寫成接口的形式分離出去呢?Good Idea! 不過updateDisplay之所以產(chǎn)生變化,不是像Duckflyable那樣是功能性的變化,而是updateDisplay方法體本身中的執(zhí)行過程需要增減。所以即使把updateDisplay作為接口分離出去,這個(gè)接口也要不斷的改變,多以我們對(duì)這類增減某一類似問題所產(chǎn)生的變化引入observer pattern

          所謂的observer pattern即是實(shí)現(xiàn)觀察者和觀察對(duì)象之間關(guān)系的一種模式。該模式中,觀察對(duì)象可以有一個(gè),也可以有多個(gè)。如果觀察者要實(shí)時(shí)接受觀察對(duì)象的信息,就必須通過該觀察對(duì)象的registerObserver方法注冊(cè)成為該觀察對(duì)象的觀察者群。也可以通過removeObserver方法注銷并離開該觀察對(duì)象的觀察者群。觀察對(duì)象通過notifyObserver方法將實(shí)時(shí)信息通知給它所有的已注冊(cè)的觀察者。

          observer pattern的本質(zhì)實(shí)質(zhì)是1對(duì)多的關(guān)系:每一個(gè)觀察對(duì)象可以對(duì)應(yīng)著許多觀察者,而每一個(gè)觀察者又可以觀察許多觀察對(duì)象

           

          把這種思路放到目前我們需要解決的氣象站的例子上去:

           

          較前面的實(shí)現(xiàn)方式,主要變化的代碼如下:

          public interface Subject
          {
               //
          之所以要寫一個(gè)接口而不直接使用WeatherData類,是因?yàn)樵摻涌谙旅婵赡懿恢?/span>WeatherData這一個(gè)實(shí)現(xiàn)對(duì)象
              public void registerObserver(
          Observer o);

              public void removeObserver(Observer o);

              public void notifyObservers();
          }

           

          import java.util.ArrayList;

          public class WeatherData implements Subject
          {

              private float temperature;
              private float humidity;
              private float pressure;
              private ArrayList<Observer> observers = new ArrayList<Observer>();

              public WeatherData()
              {
              }

              public void setMeasurement(float temperature, float humdity, float pressure)
              {   //
          氣象站使用該方法設(shè)置新的氣象數(shù)值,并通知所有已注冊(cè)觀察者
                  this.temperature = temperature;
                  this.humidity = humdity;
                  this.pressure = pressure;
                  notifyObservers();
              }

              public void registerObserver(Observer o) //注冊(cè)觀察者
              {
                  observers.add(o);
              }

              public void removeObserver(Observer o) //注銷觀察者
              {
                  if (observers.indexOf(o) >= 0)
                  {
                      observers.remove(o);
                  }
              }

              public void notifyObservers() //通知所有的已注冊(cè)的觀察者新信息
              {
                  for (Observer o : observers)
                  {
                      o.update(temperature, humidity, pressure);
                  }
              }
          }

          public class Main
          {
              public static void main(String[] args)
              {
                  WeatherData wd=new WeatherData();
                  CurrentCondition cc=new CurrentCondition();
                  wd.registerObserver(cc); //
          注冊(cè)CurrentCondition顯示方式到WeatherData的觀察者群中去
                  Statistics st=new Statistics();
                  wd.registerObserver(st);   //
          同理,注冊(cè)Statistics的顯示方式
                  wd.setMeasurement(20, 10, 15);
                  wd.setMeasurement(21, 11, 16);       
                  System.out.println("===================
          我是分割線=================");
                  wd.removeObserver(st); //
          注銷Statistics的顯示方式
                  wd.setMeasurement(19, 9, 14);
              }
          }

           

          運(yùn)行結(jié)果如下;

          Current conditions: 20.0F degrees and humidity 10.0
          Max Temperature:20.0
          Min Temperature:20.0
          Average Temperature:20.0
          Current conditions: 21.0F degrees and humidity 11.0
          Max Temperature:21.0
          Min Temperature:20.0
          Average Temperature:20.5
          ===================
          我是分割線=================
          Current conditions: 19.0F degrees and humidity 9.0

           

          由此,我們可以看到,我們能夠在Main方法中動(dòng)態(tài)的注冊(cè)和注銷某被觀察對(duì)象的已注冊(cè)的觀察者,達(dá)到了我們預(yù)期的目的。

          現(xiàn)在我們嘗試新增一個(gè)顯示方式HeatIndex,heat index是熱指數(shù),它的計(jì)算方法為:heatIndex=16.923+1.85212*temperature+5.37941*humidity

          很簡(jiǎn)單,我們只要寫一個(gè)新的類,實(shí)現(xiàn)ObserverDisplayElement接口就可以了,在主程序Main中,我們?cè)侔堰@個(gè)顯示方式注冊(cè)到WeatherData的觀察者中去。

          public class HeatIndex implements Observer, Display
          {

              private float temperature;
              private float humidity;

              public HeatIndex()
              {
              }

              public void display()
              {
                  float heatIndex = (float) (16.923 + 1.85212 * temperature + 5.37941 * humidity);
                  System.out.println("Heat Index is " + heatIndex);
              }

              public void update(float temperature, float humidity, float pressure)
              {
                  this.humidity = humidity;
                  this.temperature = temperature;
                  display();
              }
          }

          public class Main
          {

              public static void main(String[] args)
              {
                  WeatherData wd = new WeatherData();
                  CurrentCondition cc = new CurrentCondition();
                  wd.registerObserver(cc);
                  Statistics st = new Statistics();
                  wd.registerObserver(st);
                  wd.setMeasurement(20, 10, 15);
                  wd.setMeasurement(21, 11, 16);
                  System.out.println("===================
          我是分割線=================");
                  wd.removeObserver(st);
              
             HeatIndex hi = new HeatIndex();
                  wd.registerObserver(hi);  
              
                  wd.setMeasurement(19, 9, 14);
              }
          }

          運(yùn)行結(jié)果如下:

          Current conditions: 20.0F degrees and humidity 10.0
          Max Temperature:20.0
          Min Temperature:20.0
          Average Temperature:20.0
          Current conditions: 21.0F degrees and humidity 11.0
          Max Temperature:21.0
          Min Temperature:20.0
          Average Temperature:20.5
          ===================
          我是分割線=================
          Current conditions: 19.0F degrees and humidity 9.0
          Heat Index is 100.52797

          可見,我們很輕松的就添加了一個(gè)顯示方式,并動(dòng)態(tài)的注冊(cè)到相應(yīng)的觀察者中,而且最重要的是:我們沒有對(duì)源代碼做任何更改。

           

          posted on 2008-07-18 00:54 化的了 閱讀(1651) 評(píng)論(2)  編輯  收藏 所屬分類: 設(shè)計(jì)模式

          Feedback

          # re: Observer Pattern 觀察者模式 2008-07-18 11:23 Edward Li
          您好,您寫的文章很不錯(cuò),但是我有個(gè)疑問,加入subject之后里面的obsever的add方法是哪里來的。 我個(gè)人覺得你講的內(nèi)容已經(jīng)是依賴注入模式了,而observer模式僅僅是設(shè)置一個(gè)observer方法來監(jiān)控某個(gè)方法狀態(tài)轉(zhuǎn)換的模式,很顯然你講的超出了這個(gè)范圍了??偨Y(jié)一下就是你講的是依賴注入+observer.不知道你怎么看這個(gè)問題。請(qǐng)及時(shí)回復(fù)我  回復(fù)  更多評(píng)論
            

          # re: Observer Pattern 觀察者模式 2008-12-25 01:13 zkenshin
          看看head first design pattern會(huì)發(fā)現(xiàn)雷同得不得了
          如果本文作者做的工作只是翻譯 請(qǐng)說明 以免混淆  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 金昌市| 神农架林区| 建平县| 凌源市| 大足县| 尼勒克县| 长丰县| 百色市| 三亚市| 平顶山市| 郧西县| 桐乡市| 库伦旗| 张家川| 安化县| 滕州市| 班玛县| 临清市| 平凉市| 平度市| 梁河县| 辉南县| 岑溪市| 延庆县| 五河县| 海南省| 镇雄县| 新巴尔虎左旗| 禄丰县| 乐亭县| 泰州市| 蕲春县| 来宾市| 疏勒县| 麦盖提县| 务川| 聂拉木县| 高台县| 宜宾市| 秭归县| 缙云县|