最近为了做一个卡牌的项目,写了个观察模式的工具类来处理卡牌技能的效果,主要的功能是封装了发送者(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);
}
}
}