設計模式2:觀察者(Observer)模式

背景問題:假設我們現在接到一個來自氣象臺的軟件開發合約,對方希望我們開發一個天氣預報類的軟件。假設需要建立三種佈告板,分別顯示目前的狀況,氣象統計及簡單的預報。當氣象臺提供的WeatherObject對象獲得最新的測量數據時,三種佈告板必須實時更新。如氣象臺所說,他們的WeatherData源文件中有getTemperature( ),getHumidity( ),getPressure( ),measurementsChanged( )。對於前三個方法各自返回最近的氣象測量數據,我們不用在乎這些方法如何設置變量,WeatherData對象自己知道如何從氣象臺獲取更新。而最後一個方法作爲一個線索提示我們需要在這裏添加代碼,顧名思義一旦三個變量更新,這個方法會被調用。
 
順水推舟:根據氣象臺的暗示,在measurementsChanged( )方法中添加我們的代碼貌似是個不錯的選擇:
  1. public class WeatherData{   
  2.   
  3.    public void measurementsChanged(){     
  4.         float temp = getTemperature();  
  5.         float humidity = getHumidity();   
  6.         float pressure = getPressure();     
  7.    
  8.         currentConditionsDisplay.update(temp,humidity,pressure);  
  9.         statisticsDisplay.update(temp,humidity,pressure);      
  10.         forecastDisplay.update(temp,humidity,pressure);  
  11.    ] 
  12. }      
 
思考:哪裏出了問題?回想我們在設計模式1博文中討論過的設計原則,顯而易見
  1. currentConditionsDisplay.update(temp,humidity,pressure);   
  2. statisticsDisplay.update(temp,humidity,pressure);       
  3. forecastDisplay.update(temp,humidity,pressure);   
是針對具體實現在編程,這樣會導致我們以後再增加或刪除佈告板時必須修改程序,而至少,update()在這裏看起來像一個統一的接口,參數都是溫度,溼度,氣壓。         
這也就引出了我們博客討論的第三個設計原則:爲了交互對象之間的鬆耦合設計而努力。鬆耦合有利於我們建立有彈性的系統,能夠應對變化,是因爲對象之間的互相依賴降低到了最低。這符合我們這篇博客所論述的主題:觀察者模式
 
炒冷飯:觀察者模式定義了對象之間的一對多依賴,這樣一來,對一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。當思考這個定義時,我們會發現很有道理。我們的WeatherData類正是此處所說的“一”,而我們的“多”正是此處使用的天氣預測的各種佈告板。我們還必須記得,每個佈告板都有差異,這也就是爲什麼我們需要一個共同的接口的原因。儘管佈告板的類不一樣,但是它們都應該實現相同的接口,好讓WeatherData對象能夠知道如何把觀測值送給它們。顯而易見:update()方法應該在所有佈告板都實現的共同接口裏定義。
 
解決方案:
方案一:不使用JAVA爲觀察者模式提供的內置支持(理由:自己動手豐衣足食,自己建立的一切都會更具有彈性,況且建立這一切並不是很麻煩)
1.接口部分
a.主題接口(Subject.java)
  1. public interface Subject { 
  2.     public void registerObserver(Observer o); 
  3.     public void removeObserver(Observer o); 
  4.     public void notifyObserver(); 
b.觀察者接口(Observer.java)
  1. public interface Observer { 
  2.     public void update(float temp,float humidity,float pressure); 
c.顯示接口(DisplayElement)
  1. public interface DisplayElement { 
  2.     public void display(); 
2.實現部分
a.主題實現(WeatherData.java) 
  1. import java.util.*; 
  2. public class WeatherData implements Subject{ //WeatherData現在實現了Subject接口 
  3.     private ArrayList observers;//加上了ArrayList來記錄觀察者,此ArrayList是在構造函數中建立的 
  4.     private float temperature; 
  5.     private float humidity; 
  6.     private float pressure; 
  7.     public WeatherData(){ 
  8.         observers=new ArrayList(); 
  9.     } 
  10.     public void registerObserver(Observer o){ 
  11.         observers.add(o);//當註冊觀察者時,我們只要把它加到ArrayList的後面即可 
  12.     } 
  13.     public void removeObserver(Observer o){ 
  14.         int i=observers.indexOf(o);//同樣地,當觀察者想取消註冊,我們把它從ArrayList中刪除即可 
  15.         if(i>0){ 
  16.             observers.remove(i); 
  17.         } 
  18.     } 
  19.     public void notifyObserver(){//我們把狀態告訴每個觀察者,因爲觀察者都實現了update(),所以我們知道如何通知它們 
  20.         for(int i=0;i<observers.size();i++){ 
  21.             Observer observer=(Observer)observers.get(i); 
  22.             observer.update(temperature,humidity,pressure); 
  23.         } 
  24.     } 
  25.     public void measurementsChanged(){ 
  26.         notifyObserver();//當從氣象站得到更新觀測值時,我們通知觀察者 
  27.     } 
  28.     public void setMeasurements(float temperature,float humidity,float pressure){ 
  29.         this.temperature=temperature;//測試佈告板,也可以寫代碼從網站上抓取觀測值 
  30.         this.humidity=humidity; 
  31.         this.pressure=pressure; 
  32.         measurementsChanged(); 
  33.     } 
  34.     public float getTemperature(){ 
  35.         return temperature; 
  36.     } 
  37.     public float getHumidity(){ 
  38.         return humidity; 
  39.     } 
  40.     public float getPressure(){ 
  41.         return pressure; 
  42.     } 
  43.      
b.觀察者即佈告板的實現(CurrentConditionsDisplay.java) 
  1. public class CurrentConditionsDisplay implements Observer,DisplayElement{//實現了Observer接口,所以可以從WeatherData對象中獲得改變 
  2.     private float temperature; 
  3.     private float humidity; 
  4.     private Subject weatherData; 
  5.     public CurrentConditionsDisplay(Subject weatherData){//構造器需要weatherData對象(也就是主題)作爲註冊之用 
  6.         this.weatherData=weatherData; 
  7.         weatherData.registerObserver(this); 
  8.     } 
  9.     public void update(float temperature,float humidity,float pressure){ 
  10.         this.temperature=temperature;//當update()被調用時,我們把溫度和溼度保存起來,然後調用display() 
  11.         this.humidity=humidity; 
  12.         display(); 
  13.     } 
  14.     public void display(){ 
  15.         System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"%humidity"); 
  16.     } 
3.測試程序(WeatherStation.java) 
  1. import java.util.*; 
  2. public class WeatherStation { 
  3.     public static void main(String[] args) { 
  4.         WeatherData weatherData = new WeatherData(); 
  5.      
  6.         CurrentConditionsDisplay currentDisplay =  
  7.             new CurrentConditionsDisplay(weatherData); 
  8.         //StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); 
  9.         //ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); 
  10.  
  11.         weatherData.setMeasurements(806530.4f); 
  12.         weatherData.setMeasurements(827029.2f); 
  13.         weatherData.setMeasurements(789029.2f); 
  14.     } 
  15.  
4.測試界面
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章