Design Pattern -- Observer

What: 何謂觀察者模式?

--定義了物件之間一對多關係,如此一來當一個物件改變狀態,其他相依者都會收到通知並自動更新。 

 

Why: 為什麼要使用觀察者模式?

--因為當一個物件的狀態改變時,與此物件的狀態有關的其他相依物件都會被通知而做出因應的變更。 

 

When: 什麼時機適合用此模式?

--當物件的狀態改變會相對促使其他物件連帶因應改變時,就適用此模式。 

 

How: 若要使用此模式,如何使用呢?

--從上面的敘述中會發現幾個問題需要釐清:

    1. 何謂物件之間一對多的關係?

    2. 何謂物件的狀態? 何謂物件的狀態改變?

    3. 何謂與物件的狀態有關的其他相依物件? 何謂連帶因應改變?

    4. 何謂收到通知並自動更新?

 別急,透過下面舉的範例,會從中為使用者一一解答!


在舉例之前先說明一個小小的觀念~ 『報社的運作模式』 

 在暢談何謂觀察者模式前,我們要先了解一家報社的運作模式,為什麼? 看下去就知道了。一家報社的運作模式為何?

 可以分為4個部分,以下 

 1:報社的業務就是出版報紙 

 2:客戶()向某家報社訂閱報紙,只要報社(他們)有報紙出版,就會送一份給訂閱戶名單上的客戶們,只要你有在清單上,就會持續收到新報紙。 

 3:當客戶()不想再看報紙時,只要取消訂閱,報社(他們)就不會再送報紙給你,因為在訂閱戶名單上,你同時也會被除名了。

 4: 只要報社還在營運,就會一直有客戶向報社訂閱報紙取消訂閱


其實,發現了嗎? 一家報社的運作就是一個典型的觀察者模式的縮影!!

什麼?沒發現...這可不是國王的新衣,只有聰明的人才看的到。報社的運作模式已經清楚的闡述了觀察者模式的定義,只是換句話說而已,不信? 往下看...

 再次拿出觀察者模式的定義來比較一下:

--定義了物件之間一對多關係,如此一來當一個物件改變狀態,其他相依者都會收到通知並自動更新。

 

如何換句話說,神奇的傑克??

傑克: 物件 = 報社

傑克: 一對多的關係  = 報社(1)出版報紙給客戶(n)的關係

傑克: 物件改變狀態 = 報社出版的報紙

傑克: 其他相依者 = 訂閱報紙的客戶們

傑克: 收到通知並自動更新 = 在訂閱戶名單上的客戶們都會收到新報紙

!!傑克,這真是太神奇了!!報社的運作模式還真的是觀察者模式的換句話說而已呢!!

 而且似乎也為我們解答了一開始有待釐清的問題!!恩,我想上述的小小觀念讓我們好像對觀察者模式更加了解,但又有種似懂非懂的感覺,有了這個基礎的觀念,讓我們更深入與具體的對觀察者模式的定義做解釋,並且更鉅細靡遺的把答案找出來。

上述一個報社的運作其實就是觀察者模式的縮影,何解?如下圖有出版者(Publishers:如報社)加上訂閱者(Subscribers:如客戶)就可以構成觀察者模式。因為客戶會向報社做出訂閱取消訂閱的動作/要求,而報社就是出版報紙,當有新報紙要出版時,就會通知並發送在訂閱用戶單上的所有客戶們新的報紙。

 有了基本的認知,就可以再延伸!

 所以廣義的延伸,出版者可以換成一個預被關注的主題”(Subject),而訂閱者可以換成預觀察主題的觀察者”(Observer),這樣的解釋是否更廣泛也不會難以理解!

 例如:今天我有一個主題物件(Subject Object),這個主題的內容是一個整數2,以及數個觀察者物件(Observer Objects);如狗物件(Dog Object)、貓物件(Cat Object)、老鼠物件(Mouse Object)。這幾個觀察者物件已經向主題物件申請訂閱/註冊,希望可以觀察主題物件的內容,所以主題物件會將內容提供給有訂閱/註冊的觀察者們。不僅如此,當主題物件的內容有所變動時,也會立即通知並傳送最新資訊給對方。

  Observer.JPG

下圖1~6為一觀察者模式運作的分解圖,讓我為各位依序解說:

延續上面的說明,倘若今天多一個預觀察主題內容的觀察者,如下圖鴨子物件(Duck Object),那鴨子物件就要先向主題物件申請訂閱/註冊。

1

 鴨子物件順利訂閱/註冊後,隨即會被加入觀察者名單上(就是像報社的訂閱用戶名單一樣)


2

當主題物件的內容變動時,如下圖從原本的2變更成8,則主題物件就會通知並傳送變更過後的內容給觀察者名單上的觀察者們。

3

在任一時間點,倘若有任何一個觀察者不想再繼續關注主題物件的內容時,只要再次向主題物件提出取消訂閱/取消註冊的申請,如下圖的老鼠物件(Mouse Object)

4

則老鼠物件(Mouse Object)這個觀察者,就會從觀察者名單上被移除掉了。


 5


即使主題物件的內容再次變更,由於老鼠物件已經不在觀察者者名單上,所以就不會被通知,也不會取得變更後的內容了。

 

6

 透過上述的圖解,讀者對一觀察者模式的運作應有更透徹的了解,現在就讓我們結合上述的說明,一一釐清先前所提出的問題吧!

 Q:何謂物件之間一對多的關係?

 A:藉由上述的解說,說的白話一點就是,一個主題物件與多個對它內容有興趣的觀察者物件們的關係,如下圖。

Q:何謂物件的狀態? 何謂物件的狀態改變?

A:其實物件的狀態就是指主題的內容,當內容有所改變時,也可稱為狀態改變,如下圖。

Q:何謂與物件的狀態有關的其他相依物件? 何謂連帶因應改變?

A:簡單來說就是對主題內容有關注的所有觀察者們,就稱為相依物件。為什麼稱為相依物件?因為這些觀察者們可能會將主題所提供的內容做一些延伸的應用,想當然而,當主題內容變更時,也會影響到觀察者們連帶因應改變,所以觀察者們與主題是有相依性的。

Q:何謂收到通知並自動更新?

A:當主題內容有所改變時,就會自動將新的內容發送給在觀察者名單上的所有觀察者,而當觀察者收到來自主題的通知後(在此例為收到新的容),便會有相對應的動作,此動作可以為更新自身的內容(就是將主題傳送來的新內容另存於自身的參數中),也稱為自動更新(至於更新只是個統稱,要做什麼看個人拉XD)


~這麼多哩哩咂咂的解說都是概念性的,但終究還是要回歸到寫CODE的層面,那就廢話不多說,直接看觀察者模式的架構圖吧,如下圖。

觀察者模式由主題(Subject)物件與觀察者(Observer)物件所組成,分別都提供了一個抽象介面(Interface),再由子類別(ConcreteSubject(實體主題)ConcreteObserver(實體觀察者))去實踐實體物件並重新定義介面所提供的函式。一個主題物件會透過合成(Has a)擁有多個觀察者物件,這也是先前提到的一對多的關係,而一個觀察者物件也會透過合成(Has a)擁有一個主題物件。為什麼? 不然主題要怎麼通知觀察者名單上的觀察者們,如果它自己都不認識或沒有他們?! 觀察者要向誰註冊? 如果不知道主題是誰的話?!

 


既然了解了觀察者模式的架構圖,那當然要應用一下囉,哇~終於要舉例了!!

[範例] 實踐一個電子氣象公佈欄 

假設有一個氣象站(Weather Station),這個氣象站會偵測到當天每小時的濕度、溫度以及氣壓等資料,我們希望可以得到這些資料(透過WeatherData object物件),並用在我們自己的電子氣象公佈欄(Display device)上,該如何做呢? 使用觀察者模式,那架構圖又應該是什麼樣子? 為什麼呢? 首先,我們要有能力辨別誰是主題,誰是觀察者! 就這個範例來看,WeatherDataObject會是一個主題(Subject),因為它可以取得氣象站的內容(溫度、濕度及氣壓),而一個電子氣象公佈欄會是一個觀察者(Observer),因為它對WeatherDataObject所取得的資料感興趣,並希望得到此資料做延伸的應用,那就是展示出來。

在辨別了主題以及觀察者後,我們就可以繪出架構圖,如下圖。其中(1)是主題的抽象介面; (2)是觀察者的抽象介面; (3)是電子氣象公佈欄的抽象介面; (4)WeatherData;是實踐主題的實體物件; (5)是實踐觀察者與電子氣象公佈欄的實體物件。


看完類別圖後,會按圖說故事了嗎始終還是不知從何開始嗎那由我來示範一下。從前從前....恩,不是XD 不胡鬧了,談正經事。故事開始於,我們希望可以從氣象站得到資料,好呈現在我們自己的電子氣象公佈欄上...

首先,WheatherData要先繼承主題抽像介面(上圖(1)),好實做註冊成為觀察者(registerObserver)、取消成為觀察者(removeObserver)以及通知觀察者(notifyObserver)等由(1)所提供的函式。WheatherData同時也提供一個公開(public)的函式(setmeasurement)給使用者操作,當使用者從外部呼叫此函式並設定新的資料時,則此函式內就會通知觀察者名單上所有的觀察者,並將新的資料傳送給他們。

再來,CurrentconditionDisplay要繼承觀察者介面(上圖(2))以及電子氣象公佈欄介面(上圖(3)),好實做(2)所提供的更新(update)以及(3)所提供的呈現(display)等函式。當CurrentconditionDisplay收到由主題所通知後,就自動呼叫更新函式(update)進行資料的更新,並呼叫呈現函式(display),將資料顯示在電子公佈欄上。

故事說完了,公主與王子從此過著幸福快樂的日子,讓我們看看他們多開心。瞧,看main主程式就知道,簡潔明瞭,用得多開心阿,只要簡單幾個步驟,就可以長期關注一個議題(主題),而當不想關注時,只要取消關注,就不用再收到垃圾資料了!

Observer13.JPG 

[Note]

觀察者模式的限制是,當主題一變更,就會通知觀察者名單上所有的觀察者更新,不可以依賴特定的通知次序,也不能指定通知。簡單來說就是名單上的一起通知,順序也不可指定,也不能特定只通知誰。

在遊戲中的應用非常的廣泛,在遊戲中效能是很重要的一項指標,如果所有的物件都必須主動偵測感興趣的物件是否有發生什麼改變,是很麻煩的一件事,如此你就會發現你的程式碼到處都是if...else...等等的判斷式。主動偵測其他物件的狀態則變得像當沒有效率,(if...else...可能是一個問題,另外一點是狀態並不一定隨時再改變,沒有改變也在偵測將是很沒意義的事),因此改為被動的方式來接收訊息,當物件自己的狀態改變時,通知有需要執行更新的物件,平常一直在偵測物件狀態的物件,變成被動接收訊息或是指令的,不再主動偵測,而是被呼叫該做什麼事,當然,要做什麼事也是事先告知(註冊)對方的。

像是事件處理,碰撞偵測,UI,Input等等都是利用Observer的機制來達到訊息溝通與處理的機制。


#所有圖片剪輯自Head First Design Pattern一書

arrow
arrow
    全站熱搜

    kgsprogrammer 發表在 痞客邦 留言(0) 人氣()