Head First 設計模式之觀察者模式(Observer Pattern)

前言:java

      這一節開始學習觀察者模式,開始講以前會先像第一節那樣經過一個應用場景來引入該模式。具體場景爲:氣象站提供了一個WeatherData對象,該對象能夠追蹤獲取天氣的溫度、氣壓、溼度信息,WeatherData對象會隨即更新三個佈告板的顯示:目前情況(溫度、溼度、氣壓)、氣象統計和天氣預報。編程

1.    基本需求:利用WeatherData對象獲取數據、並更新三個佈告板:目前情況、氣象統計和天氣預報

WeatherData類圖以下:學習

 

說明:測試

     GetTemperature()、GetHumidity()、GetPressure()分別用來獲取天氣溫度、溼度氣壓,MeasurementsChanged()當氣象測量更新此方法會被調用。this

分析:spa

  1. 能夠經過WeatherData的Getter方法獲取三個測量值:溫度、溼度、氣壓
  2. 氣象測量更新時會調用MeasurementsChanged()方法
  3. 須要實現三個天氣數據佈告板:「目前情況」、「氣象統計」、「天氣預報」,一旦WeatherData測到新值,這些佈告也立刻更新
  4. 此係統可擴展,能夠隨意的添加或者刪除任何佈告板。

實現:設計

public class WeatherData
{
    public void MeasurementsChanged()
    {
        float temp = GetTemperature();
        float humidity = GetHumidity();
        float pressure = GetPressure();
        currentConditionDisplay.update(temp, humidity, pressure);
        statisticsDisplay.update(temp, humidity, pressure);
        forecastDisplay.update(temp, humidity, pressure);
    }
    //其餘方法
}

反思:code

咱們的這種實現有何不妥?server

結合咱們第一節中的一些面向對象的原則,這種實現方式會有以下問題:對象

l  針對具體實現編程,後續新增或者刪除佈告板必須修改程序

l  未將改變的地方封裝起來

這種實現方式與面向對象的一些基本原則是相違背的,目前暫時是實現了用戶的需求,可是在後續不具有可擴展行。

2.    引入觀察者模式

2.1 出版者+訂閱者=觀察者模式

出版者:就至關於「主題」(Subject),訂閱者至關於「觀察者」(Observer)

當出版者(主題)發行新的報紙的時候,全部的觀察者(訂閱者)就能夠收到最新的報紙,同時,當新的觀察者(訂閱者)加入時,也能夠收到最新的報紙,當觀察者(訂閱者)退訂報紙後,就再也收不到新的報紙。

2.2 定義觀察者模式

觀察者模式定義了對象之間一對多依賴,這樣一來,當一個對象狀態改變時,它的全部依賴者都會收到通知並自動更新。

主題和觀察者定義一對多的關係。觀察者依賴於此主題,只要主題狀態一有變化,觀察者就會被通知,根據通知的風格,觀察者可能所以新值而更新。

觀察者模式:類圖

 

2.3設計原則:爲了交互對象之間的鬆耦合設計而努力

鬆耦合的威力

當兩個對象之間鬆耦合,它們依然能夠交互,可是不清楚彼此的細節。

觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。

說明:

  1. 主題只知道觀察者實現了某個接口(IObserver接口),不須要知道觀察者是誰,或其餘細節。
  2. 任什麼時候候均可以增長或者刪除的觀察者,主題惟一依賴的是一個實現了IObserver接口的對象列表。
  3. 新的類型觀察者出現時,主題代碼不須要修改,只須要在新類型裏實現觀察者接口,而後註冊爲觀察者便可。
  4. 能夠獨立的複用主題或觀察者,由於兩者鬆耦合。
  5. 改變主題或者觀察者,並不會影響另外一方。由於兩者鬆耦合。

鬆耦合的設計之因此能讓咱們創建有彈性的OO系統,可以應對變化,是由於對象之間的相互依賴講到了最低。

3.    利用觀察者模式設計並實現氣象站

3.1 設計氣象站

類圖:

 

3.2具體實現

3.2.1主題、觀察者、顯示接口
/// Description:對象、觀察者、顯示接口
    /// </summary>
    public interface ISubject
    {
        void RegisterObserver(IObserver o);//註冊觀察者
        void RemoveObserver(IObserver o);//刪除觀察者
        void NotifyObervers();//通知觀察者
    }
 
    public interface IObserver
    {
        void Update(float temp, float humidity, float pressure);
    } 

    public interface IDisplayElement
    {
        void Display();
    }

 

3.2.2 WeatherData類:註冊、刪除、通知觀察者
    /// Description:WeatherData 註冊、刪除、通知觀察者
    /// </summary>
    public class WeatherData:ISubject
    {
        private ArrayList observers;
        private float temperature;
        private float humidity;
        private float pressure;

        public WeatherData()
        {
            observers = new ArrayList();//初始化obervers,用來存儲註冊的觀察者
        }

        /// <summary>
        /// 註冊觀察者
        /// </summary>
        /// <param name="o"></param>
        public void RegisterObserver(IObserver o)
        {
            observers.Add(o);
        }

       /// <summary>
       /// 刪除觀察者
        /// </summary>
        /// <param name="o"></param>
        public void RemoveObserver(IObserver o)
       {
            int i = observers.IndexOf(o);
            if (i >= 0)
                observers.Remove(o);
        }

        /// <summary>
        /// 通知觀察者
        /// </summary>
        public void NotifyObervers()
        {
            foreach (IObserver o in observers)
            {
                o.Update(temperature, humidity, pressure);
            }
        }

        /// <summary>
        /// 當從氣象站獲得更新觀測值時,通知觀察者
        /// </summary>
        public void MeasurementsChanged()
        {
            NotifyObervers();
        }


        /// <summary>
        ///
        /// </summary>
        /// <param name="temperature"></param>
        /// <param name="humidity"></param>
       /// <param name="pressure"></param>
        public void SetMeasurements(float temperature, float humidity, float pressure)
        {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            MeasurementsChanged();
        }
    }

 

3.2.3 佈告板類,實現了IObserver、IDisplayElement接口

 

    /// Description:建立佈告板
    /// </summary>
    public class CurrentConditionsDisplay:IObserver,IDisplayElement
    {
        private float temperature;
        private float humidity;
        private ISubject weatherData;

        public CurrentConditionsDisplay(ISubject weatherData)
        {
            this.weatherData = weatherData;
            weatherData.RegisterObserver(this);
        }

        public void Update(float temperature, float humidity, float pressure)
        {
            this.temperature = temperature;
            this.humidity = humidity;
            Display();
        }
        public void Display()
        {
            Console.WriteLine("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");
        }
    }

 

3.2.4 測試
WeatherStation.WeatherData weatherData = new WeatherStation.WeatherData();
WeatherStation.CurrentConditionsDisplay currentDisplay = new WeatherStation.CurrentConditionsDisplay(weatherData);
weatherData.SetMeasurements(10, 20, 30);

 

結果以下:

 

4.    Java內置的觀察者模式

Java內置的觀察者模式,許多功能都已經事先準備好了,甚至能夠用推(push)或拉(pull)的方式傳送數據。

使用java內置觀察者模式實現氣象站的OO設計類圖,以下:

 

Java內置觀察者模式與咱們在3小節中明顯的差別是WeatherData繼承自Observable類,並集成了一些增長、刪除、通知觀察者的方法。

l  將對象變成觀察者

首先仍是要實現Observer(觀察者)接口,其次調用Observable對象的addObserver()方法便可。

l  可觀察者(主題)送出通知

  1. 先調用setChanged()方法,標記狀態已經改變的事實。
  2. 調用notifyObservers()方法(該方法有2個,任意一個皆可):notifyObservers()或notifyObservers(Object arg)

l  觀察者接收通知

觀察者實現了Observer的接口,方法簽名以下:

update(Observable o,Object arg)

第一個參數爲主題自己,讓觀察者知道是哪一個主題通知它的

第二個參數是傳入notifyObservers()的數據對象

若是想用「推」的方式將數據給觀察者,則能夠把數據當作數據對象的方式傳給notifyObservers(arg)

若是想用「拉」的方式將數據給觀察者,則須要在update()中,經過WeatherData的getTemperature()等方法獲取對應的氣象值。

缺陷:

Java內置的觀察者模式中Observable是一個類,你必須設計一個類去繼承它。若是某類相同時具備Observable類和另外一個超類的行爲,就沒法實現,由於java不支持多繼承。

同時也限制了Observable的複用能力。

同時,Observable API將setChanged()方法保護了起來,除非繼承自Observable類,不然沒法建立Observable實例組合到本身的對象中,也違背了面向對象設計的第二個原則:多用組合,少用繼承。

5.    總結

OO原則:

封裝變化

多用組合,少用繼承

針對接口編程,不針對實現編程

對交互對象之間的鬆耦合設計而努力(新的OO原則,鬆耦合的設計更有彈性,更能應對變化)

OO模式:

觀察者模式—在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。

相關文章
相關標籤/搜索