作者:王選易,出處:http://www.cnblogs.com/neverdie/ 歡迎轉載,也請保留這段聲明。如果你喜歡這篇文章,請點【推薦】。謝謝!
一對多的觀察者模式機制有什么缺點?
想要查看完整源代碼,還是訪問這個項目的GitHub:https://github.com/MrNerverDie/Unity-Flappy-Bird
如果你對如何在Unity中使用事件/委托機制還不太了解,建議您查看我的前一篇文章:【Unity3D技巧】在Unity中使用事件/委托機制(event/delegate)進行GameObject之間的通信
在前一篇博客里面,我們寫到:【Unity3D技巧】在Unity中使用事件/委托機制(event/delegate)進行GameObject之間的通信,其中使用了EventHandler這個委托作為通用的事件類型,實現了一對多的觀察者模式。但是這樣做有什么缺點呢?我們還是舉前文所說的小鳥撞上管道為例:
在這里面,小鳥撞倒管道時,小鳥會發送一個 產生碰撞的 消息傳送給所有觀察者,那么,如果我們新加入一個名為天鵝,它也能在天上飛,那么我們就要在它的類內部實現一個同樣功能的 產生碰撞的 消息傳送給所有觀察者,這樣就產生了代碼重復,軟件中解決代碼重復的問題一般使用的事引入中間層的辦法,所以我仿照Cocos2d-x中的CCNotificationCenter寫了一個CCNotificationCenter來存儲各種消息並轉發來當作中間層。
引入中間層 -- NotificationCenter
NotificationCenter基本的設計思路是基於MessageDispatcher模式的,即使用一個字典(Dictionary)來記錄各種需要轉發的信息,以及這些信息的觀察者,然后再恰當的時候進行消息的轉發。NotificationCenter還應當提供觀察者訂閱和取消訂閱的方法。
NotificationCenter是基於單件模式的,它在第一次被調用GetInstance方法時被初始化的,使用單件的原因是要讓NotificationCenter的生命期比任何一個觀察者都長,這樣才不會出現NotificationCenter為空的情況。
在下面的代碼里,我把INotificationCenter做成抽象類的原因是:希望通過這個繼承這個類來創建多個子類,比如可以創建UINotificationCenter,BattleNotificationCenter,TradeNotificationCenter。來進行消息的分組。
下面我把自己的代碼分享一下,供大家參考:
using UnityEngine; using System; using System.Collections; using System.Collections.Generic; // NotificationCenter的拓展類,在這里弄出多個INotificationCenter的子類, // 分別處理不同的消息轉發,便於消息分組 public class NotificationCenter : INotificationCenter { private static INotificationCenter singleton; private event EventHandler GameOver; private event EventHandler ScoreAdd; private NotificationCenter() : base() { // 在這里添加需要分發的各種消息 eventTable["GameOver"] = GameOver; eventTable["ScoreAdd"] = ScoreAdd; } public static INotificationCenter GetInstance() { if (singleton == null) singleton = new NotificationCenter(); return singleton; } } // NotificationCenter的抽象基類 public abstract class INotificationCenter { protected Dictionary<string, EventHandler> eventTable; protected INotificationCenter() { eventTable = new Dictionary<string, EventHandler>(); } // PostNotification -- 將名字為name,發送者為sender,參數為e的消息發送出去 public void PostNotification(string name) { this.PostNotification(name, null, EventArgs.Empty); } public void PostNotification(string name, object sender) { this.PostNotification(name, name, EventArgs.Empty); } public void PostNotification(string name, object sender, EventArgs e) { if (eventTable[name] != null) { eventTable[name](sender, e); } } // 添加或者移除了一個回調函數。 public void AddEventHandler(string name, EventHandler handler) { eventTable[name] += handler; } public void RemoveEventHandler(string name, EventHandler handler) { eventTable[name] -= handler; } }
對觀察者進行抽象化 -- 引入Observer
在加入了NotificationCenter之后,我們要面對的下一個問題就是,我們的每一個觀察者都需要在自己的Start方法中添加回調函數,在OnDestroy方法中取消回調函數,那么,我們可以把這部分的代碼抽象在一個Observer組件中,使用另一個字典記載下所有的該GameObject注冊的回調函數,在Observer的OnDestroy方法里面一次全部取消訂閱。代碼如下:
using UnityEngine; using System.Collections; using System.Collections.Generic; using System; public class Observer : MonoBehaviour { private INotificationCenter center; private Dictionary<string, EventHandler> handlers; void Awake() { handlers = new Dictionary<string, EventHandler>(); center = NotificationCenter.GetInstance(); } void OnDestroy() { foreach (KeyValuePair<string, EventHandler> kvp in handlers) { center.RemoveEventHandler(kvp.Key, kvp.Value); } } public void AddEventHandler(string name, EventHandler handler) { center.AddEventHandler(name, handler); handlers.Add(name, handler); } }