1. 程式人生 > 其它 >Head First 設計模式——觀察者模式

Head First 設計模式——觀察者模式

Head First 設計模式——觀察者模式

tags: 設計模式

本章主要介紹觀察者模式。觀察者模式可以幫助物件知悉現況,不會錯過物件感興趣的事。物件甚至在執行時可決定是否要繼續被通知。觀察者模式是JDK中使用最多的模式之一。本章也會一併介紹一對多關係,以及鬆耦合。有了觀察者,訊息會更靈通。

1. 認識觀察者模式

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

觀察者模式類似於報紙的訂閱,只是名稱不太一樣:出版者改稱為"主題"(Subject),訂閱者改稱為"觀察者"(Observer)。

主題物件:主題物件管理某些資料

觀察者物件:觀察者訂閱(註冊)主題以便在主題資料改變時能夠收到更新

當主題內的資料發生改變時,新的資料就會以某種形式送到觀察者手上


2. 定義觀察者模式

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

實現觀察者模式的方法不止一種,但是以包含Subject與Observer介面的類設計的做法最常見。


3. OO設計原則

為了互動物件之間的鬆耦合設計而努力


4. 鬆耦合

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

當兩個物件之間鬆耦合,它們依然可以互動,但是不必清楚彼此的細節。

鬆耦合的設計能建立有彈性的OO系統,因為物件之間的互相依賴降到了最低。


5. Java內建的觀察者模式

5.1 Observer介面和Observable類

Java API有內建的觀察者模式。java.util包中包含最基本的Observer介面和Observable類,Observer介面和Observable類使用上更加方便,因為許多功能都已經事先實現過了。通過使用推(push)或拉(pull)的方式傳送資料。

由於Java內建的支援,只需擴充套件(繼承)Observable,並告訴它核實該通知觀察者,剩下的事API會幫助你完成。

Observable類中的方法有:

  • addObserver()
  • deleteObserver()
  • notifyObserver()
  • setChanged()

Observable是一個"類",而非介面。Observable類會追蹤所有的觀察者,並通知它們。之前的"主題"(Subject),也稱為"可觀察者"(Observable)繼承自Observable類。在繼承的類(主題)中不需要再提供register()、remove()和notifyObservers()方法,因為已經從超類(Observable)中繼承了。

5.2 Java內建的觀察者模式

5.2.1 Java內建觀察者模式與觀察者模式的差別

主題擴充套件自Observable類,並繼承了增加、刪除、通知觀察者的方法以及其他方法。

5.2.2 Java內建觀察者模式的使用

  • 將物件變成觀察者:實現觀察者介面(java.util.Observer),然後呼叫任何Observable物件的addObserver()方法。當不想再當觀察者時,呼叫deleteObserver()方法即可。

  • 可觀察者(主題)傳送資料

    • 利用擴充套件java.util.Observable類產生"可觀察者"

    • 呼叫setChanged()方法,標記狀態已經改變

    • 呼叫notifyObservers()或notifyObservers(Object arg)

      如果想"推"(push)資料給觀察者,就把資料當做資料物件傳送給notifyObservers(Object arg)方法。否則,觀察者就必須從可觀察者物件中"拉"(pull)資料。

  • 觀察者接受資料:觀察者實現更新的方法

    update(Observable o,Object arg)
    //Observable o:主題(可觀察者)本身當做第一個變數,使觀察者知道是哪一個主題通知它的
    //Object arg:傳入notifyObservers()的資料物件。未說明則為空。
    

5.2.3 setChanged()方法

setChanged()方法用來標記已經改變的事實,讓notifyObservers()方法知道當它被呼叫時應該更新觀察者。如果呼叫notifyObservers()之前未呼叫setChanged()方法,則觀察者就"不會"被通知。

setChanged()方法的虛擬碼:

setChanged(){
    changed = true     //setChanged()方法把changed標誌設為true
}
//推資料
notifyObservers(Object arg){         
    //notifyObservers()方法只會在changed標為"true"時通知觀察者
    if(changed){
        for every observer on the list{
            call update(this,arg)
        }
        changed = false
        //通知觀察者之後,把changed標誌設回false
        //所以若未呼叫setChanged()方法,則changed預設為false,就不會通知觀察者進行更新
    }
}
//拉資料
notifyObservers(){
    notifyObservers(null)
}
//clearChanged():將changed狀態設定回false
//hasChanged():得到changed標誌當前的狀態值(true or false)

5.3 Java內建觀察者模式的使用步驟

  1. 匯入正確的Observable/Observer

    import java.util.Observer;
    import java.util.Observavle;
    
  2. 繼承Observable類

    public class Xxx extends Observable{
        //程式碼
    }
    
  3. 呼叫setChanged()方法和notifyObservers()方法

    public void xxx(){
        setChanged();          //注意呼叫先後順序,setChanged()在前,notifyObservers()在後 
        notifyObservers();     //notifyObservers(Object args);
    }
    

5.4 Java內建觀察者模式的缺點

  • Observable是一個類,限制了Observable的複用潛力

    1. 因為Observable是一個類,所以必須設計一個類繼承它。如果某類想同時具有Observable類和另一個超類的行為,就會陷入兩難,畢竟Java不支援多重繼承。
    2. 因為沒有Observable介面,所以無法建立自己的實現,和Java內建的Observers API搭配使用,也無法將java.util的實現換成另一套做法的實現。
  • Observable將關鍵的方法保護起來了

    在Observable API中,setChanged()方法被定義成protected。這意味著:除非繼承自Observable,否則無法建立Observable例項並組合到自己的物件中。該設計違反了"多用組合,少用繼承"這個設計原則。


6. 觀察者模式如何遵循設計原則

6.1 設計原則:找出程式中會變化的部分,將其和固定不變的部分相分離

在觀察者模式中,會變化的是主題的狀態,以及觀察者的數目和型別。使用此模式,可以改變依賴於主題狀態的物件,而不必改變主題。

6.2 設計模式:針對介面程式設計,不針對實現程式設計

主題與觀察者都使用介面:觀察者利用主題的介面向主題註冊,主題利用觀察者介面通知觀察者。這樣既可以讓兩者之間正常運作,又同時具有鬆耦合的優點。

6.3 設計模式:多用組合,少用繼承

觀察者模式利用"組合"將許多觀察者組合進主題中。物件之間的關係並非通過繼承產生,而是在執行時利用組合的方式產生的。


7. 要點小結

  • 觀察者模式定義了物件之間一對多的關係
  • 主題(可觀察者)用一個共同的介面來更新觀察者
  • 觀察者和主題(可觀察者)之間用鬆耦合方式(loosecoupling)結合,可觀察者不知道觀察者的細節,只知道觀察者實現了觀察者介面
  • 使用觀察者模式時,既可從被觀察者處推(push)資料,也可從被觀察者處拉(pull)資料(推的方式更安全)
  • 有多個觀察者時,不可以依賴特定的通知次序