1.意圖 對象之間一對多的依賴關係,當目標對象發生改變時,所有依賴於它的對象都要得到通知並自動更新 2.別名 依賴,發佈-訂閱 3.動機 1)需要確保相互協作的對象的一致性(數據要保持一致),但一致性會使各個對象緊密耦合,減低它們的可重用性。 2)Observer模式描述瞭如何建立這種關係:一個目標 ...
1.意圖
對象之間一對多的依賴關係,當目標對象發生改變時,所有依賴於它的對象都要得到通知並自動更新
2.別名
依賴,發佈-訂閱
3.動機
1)需要確保相互協作的對象的一致性(數據要保持一致),但一致性會使各個對象緊密耦合,減低它們的可重用性。
2)Observer模式描述瞭如何建立這種關係:一個目標(Subject)可以有任意個依賴於它的觀察者(Observer),當目標發生改變的時候,所有觀察者都得到通知(Notify);觀察者響應通知(Update),會去查詢目標狀態(GetState)使其狀態與目標狀態同步一致。
3)觀察者去訂閱(Subscribe),目標去發佈(Publish)
4.適用性
1)一個抽象模型有兩方面,一方面依賴於另一方面,兩者獨立封裝在不同對象里以方便各自獨立的改變和復用
2)一個對象的改變需要通知另一個對象,而被通知的對象數量不確定
3)被通知的對象之間互相不知道對方是誰,沒有緊密耦合
5.結構 & 6.參與者
7.協作
8.效果
1)目標與觀察者之間的抽象耦合:目標不知道具體觀察者是屬於哪個類的,只知道他們符合抽象Subject的介面,具體目標本身也可以是各式各樣的,只要符合Object抽象介面即可,所以耦合是抽象和最小的,目標和觀察者可以屬於整個系統中的任意一個層次。
2)支持廣播通信:目標可以無腦廣播給所有它的觀察者,不用知道觀察者是誰,是否響應處理通知可以放在觀察者層去判斷
3)意外的更新:A觀察者不知道B觀察者的存在,A的操作導致目標改變,會通知到B,若B其實不依賴目標(定義了錯誤的依賴關係)可能會導致大量無效的通知或者通知導致B觀察者數據錯誤
9.實現
1)創建目標與觀察者之間的映射:
方法1:在目標裡面存放觀察者列表,簡單,但目標多觀察者少的時候存儲代價會高一些
方法2:用一個公共的對象來存儲subjects 和 observers之間的映射
2)觀察多個目標:
Update裡面傳參數Subject,來告知觀察者是哪個目標更新了
3)誰觸發更新:
方式1:目標對象的屬性變更時由目標觸發更新通知,好處觀察者和其他對象不用關心何時觸發更新不會出現遺漏,缺點是一個操作可能會導致多次更新,也會發多次通知
方式2:由客戶來觸發更新通知,好處可以隨時控制觸發更新時機,避免中間更新,缺點會遺漏觸發
4)對已刪除目標的懸掛引用:
通知目標的所有觀察者把目標從身上的引用移除
5)在發出通知前確保目標的狀態自身是一致的:
方式1:Subject上的Notify在SetState之後調用,因為Observer裡面的Update可能會從Subject上GetState
方式2:用Template Method模式,即父類Subject中的模板方法把重定義操作Operation()放在Notify()之前,子類只要重定義Operation方法即可,就能確保操作完了才Notify
6)通知模型 推/拉
push推模型:把詳細信息都通知給所有觀察者,不管他們是否需要;推模型假定知道觀察者需要什麼,但假定不一定正確可能使得觀察者難以復用(收到了大量無用信息,自己想要的又沒有推送)
pull拉模型:只發送最小通知(如告訴觀察者有更新了),觀察根據自己需要從目標取他想要的數據;拉模型假定目標不知道觀察者,但效率會低,因為在目標發送信息太少的情況下觀察者無法知道目標到底改變了什麼
7)顯式的指定關心內容
void Subject::Attach(Observer *, Aspect& interest); 觀察者註冊時指定關心的目標方面變更,目標只通知關心改變方面的觀察者,併在Update里把Subject和Aspect都帶上
這個操作在Observer端做篩選其實也行。
8)封裝複雜的更新語義
更新管理器ChangeManager的職責,是個Mediator,Singleton
a) 它將一個目標映射到它的觀察者並提供一個介面來維護這個映射。這就不需要由目標來維護對其觀察者的引用, 反之亦然。
b) 它定義一個特定的更新策略。(比如N個目標都改變了才觸發通知,或者目標在400ms發生改變只觸發一次)
c) 根據一個目標的請求, 它更新所有依賴於這個目標的觀察者。
9)結合目標類和觀察者類:
兩者可以放到一個類里
10.日常開發項目應用
1)跨服邏輯,gameserver是觀察者,centerserver是目標;gameserver連上後會Attach到centerserver,斷開就Detach, centerserver上的globalserver列表發生更新的時候需要Notify所有的gameserver,用於更新PK服列表信息
2)戰鬥邏輯,player是觀察者,player和monster是目標entity;entity進入player可視範圍Attach,離開Detach,entity移動後坐標發生變更,需要通知他周圍的觀察者player坐標變更,這裡使用了ChangeManager(map)來管理這個依賴關係的映射,並且制定了更新策略:發生變更不立馬通知,400ms tick的時候如果有變更則通知觀察者