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內建觀察者模式的使用步驟
-
匯入正確的Observable/Observer
import java.util.Observer; import java.util.Observavle;
-
繼承Observable類
public class Xxx extends Observable{ //程式碼 }
-
呼叫setChanged()方法和notifyObservers()方法
public void xxx(){ setChanged(); //注意呼叫先後順序,setChanged()在前,notifyObservers()在後 notifyObservers(); //notifyObservers(Object args); }
5.4 Java內建觀察者模式的缺點
-
Observable是一個類,限制了Observable的複用潛力
- 因為Observable是一個類,所以必須設計一個類繼承它。如果某類想同時具有Observable類和另一個超類的行為,就會陷入兩難,畢竟Java不支援多重繼承。
- 因為沒有Observable介面,所以無法建立自己的實現,和Java內建的Observers API搭配使用,也無法將java.util的實現換成另一套做法的實現。
-
Observable將關鍵的方法保護起來了
在Observable API中,setChanged()方法被定義成protected。這意味著:除非繼承自Observable,否則無法建立Observable例項並組合到自己的物件中。該設計違反了"多用組合,少用繼承"這個設計原則。
6. 觀察者模式如何遵循設計原則
6.1 設計原則:找出程式中會變化的部分,將其和固定不變的部分相分離
在觀察者模式中,會變化的是主題的狀態,以及觀察者的數目和型別。使用此模式,可以改變依賴於主題狀態的物件,而不必改變主題。
6.2 設計模式:針對介面程式設計,不針對實現程式設計
主題與觀察者都使用介面:觀察者利用主題的介面向主題註冊,主題利用觀察者介面通知觀察者。這樣既可以讓兩者之間正常運作,又同時具有鬆耦合的優點。
6.3 設計模式:多用組合,少用繼承
觀察者模式利用"組合"將許多觀察者組合進主題中。物件之間的關係並非通過繼承產生,而是在執行時利用組合的方式產生的。
7. 要點小結
- 觀察者模式定義了物件之間一對多的關係
- 主題(可觀察者)用一個共同的介面來更新觀察者
- 觀察者和主題(可觀察者)之間用鬆耦合方式(loosecoupling)結合,可觀察者不知道觀察者的細節,只知道觀察者實現了觀察者介面
- 使用觀察者模式時,既可從被觀察者處推(push)資料,也可從被觀察者處拉(pull)資料(推的方式更安全)
- 有多個觀察者時,不可以依賴特定的通知次序