最近为了做一个卡牌的项目,写了个观察模式的工具类来处理卡牌技能的效果,主要的功能是封装了发送者(sender)的筛别,可以在消息内容筛别的基础上,用发送者作为进一步筛别的条件,并且可以将自己作为传参。把原本一对多的委托,封装为多对多。
工具类本身还没有正式投入使用,可能还存在并发方面的bug
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Mekong.Notifications { /// <summary> /// This delegate is similar to an EventHandler: /// The first parameter is the sender, /// The second parameter is the arguments / info to pass /// </summary> using Handler = System.Action<System.Object, Component>; /// <summary> /// The SenderTable maps from an object (sender of a notification), /// to a List of Handler methods /// * Note - When no sender is specified for the SenderTable, /// the NotificationCenter itself is used as the sender key /// </summary> using SenderTable = System.Collections.Generic.Dictionary<System.Object, System.Action<System.Object, Component>>; public class NotificationCenter : UnitySingleton<NotificationCenter> { #region Properties /// <summary> /// The dictionary "key" (string) represents a notificationName property to be observed /// The dictionary "value" (SenderTable) maps between sender and observer sub tables /// </summary> private Dictionary<string, SenderTable> _table = new Dictionary<string, SenderTable>(); //private HashSet<List<Handler>> _invoking = new HashSet<List<Handler>>(); #endregion #region Singleton Pattern private NotificationCenter() { } #endregion #region Public public void AddObserver(Handler handler, string notificationName) { AddObserver(handler, notificationName, null); } public void AddObserver(Handler handler, string notificationName, System.Object sender) { if (handler == null) { Debug.LogError("Can't add a null event handler for notification, " + notificationName); return; } if (string.IsNullOrEmpty(notificationName)) { Debug.LogError("Can't observe an unnamed notification"); return; } if (!_table.ContainsKey(notificationName)) _table.Add(notificationName, new SenderTable()); SenderTable subTable = _table[notificationName]; System.Object key = (sender != null) ? sender : this; if (!subTable.ContainsKey(key)) { Handler MyDelegate = null; subTable.Add(key, MyDelegate); } Debug.Log(key); subTable[key] += handler; } public void RemoveObserver(Handler handler, string notificationName) { RemoveObserver(handler, notificationName, null); } public void RemoveObserver(Handler handler, string notificationName, System.Object sender) { if (handler == null) { Debug.LogError("Can't remove a null event handler for notification, " + notificationName); return; } if (string.IsNullOrEmpty(notificationName)) { Debug.LogError("A notification name is required to stop observation"); return; } // No need to take action if we dont monitor this notification if (!_table.ContainsKey(notificationName)) return; SenderTable subTable = _table[notificationName]; System.Object key = (sender != null) ? sender : this; if (!subTable.ContainsKey(key)) return; subTable[key] -= handler; } /// <summary> /// Clean up the table /// If one specific notofications or sender's delegate which is null,then delete it /// </summary> public void Clean() { string[] notKeys = new string[_table.Keys.Count]; _table.Keys.CopyTo(notKeys, 0); for (int i = notKeys.Length - 1; i >= 0; --i) { string notificationName = notKeys[i]; SenderTable senderTable = _table[notificationName]; object[] senKeys = new object[senderTable.Keys.Count]; senderTable.Keys.CopyTo(senKeys, 0); for (int j = senKeys.Length - 1; j >= 0; --j) { object sender = senKeys[j]; Handler handlers = senderTable[sender]; if (handlers == null) senderTable.Remove(sender); } if (senderTable.Count == 0) _table.Remove(notificationName); } } public void PostNotification(string notificationName) { PostNotification(notificationName, null); } public void PostNotification(string notificationName, Component e) { PostNotification(notificationName, null, e); } public void PostNotification(string notificationName, System.Object sender, Component e) { if (string.IsNullOrEmpty(notificationName)) { Debug.LogError("A notification name is required"); return; } // No need to take action if we dont monitor this notification if (!_table.ContainsKey(notificationName)) return; // Post to subscribers who specified a sender to observe SenderTable subTable = _table[notificationName]; if (sender != null && subTable.ContainsKey(sender)) { Handler handlers = subTable[sender]; handlers(sender, e); return; } // Post to subscribers who did not specify a sender to observe if (subTable.ContainsKey(this)) { Handler handlers = subTable[this]; handlers(sender, e); return; } } #endregion } }
用法示例
using System.Collections; using System.Collections.Generic; using UnityEngine; using Mekong.Notifications; public class Test1 : MonoBehaviour { void Start() { NotificationCenter.Instance().AddObserver(OnPushButton,"Push Hello Button"); } void OnPushButton(object sender, Component argv) { Debug.Log(argv.GetComponent<Test2>().number); } } public class Test2 : MonoBehaviour { public int number = 114125; void Update() { if (Input.anyKeyDown) { NotificationCenter.Instance().PostNotification("Push Hello Button", this); } } }