使用委托设计一个Observer

本文介绍了一种游戏内消息通知系统的实现方案,通过单例模式的 NotificationCenter 类管理消息订阅与发布,支持消息ID与多个观察者的绑定,有效降低代码耦合度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 目标:能够实现在整个游戏中的消息传递。拥有方便的消息数据获取

 使用单个数字作为一个消息的唯一ID,一个消息对应多个接受者. 

消息接受者保存两个信息,一个是这个接受者的源,另一个是响应体。

所以 用来存储 消息对应的大致结构 就是 intID---<接受源,响应体>。

using SenderTable=System.Collections.Generic.Dictionary<System.Object,System.Collections.Generic.List<System.EventHandler>>;

简化表的声明。

新建一个消息处理类

NotificationCenter.cs

{

将他做成单例的形式

#region 单例
public readonly static NotificationCenter instance = new NotificationCenter();
private NotificationCenter()
{
}
#endregion
定义结构对监听进行存储 private Dictionary<int , SenderTable> _table = new Dictionary<int , SenderTable>();


接下来提供 对这个字典的访问的接口:

private SenderTable GetSenderTable(int notificationName)

{
if (!_table.ContainsKey(notificationName))
_table.Add(notificationName , new SenderTable());
return _table[notificationName];
}
然后要提供获取执行体列表的接口

private List<EventHandler> GetObservers(SenderTable subTable , System.Object sender)
{
if (!subTable.ContainsKey(sender))
subTable.Add(sender , new List<EventHandler>());
return subTable[sender];
}

接下来提供 添加事件的的接口,主要的逻辑还是对这个字典数据的操作:

public void AddObserver(EventHandler handler , int notificationName)
{
AddObserver(handler , notificationName , null);
}
public void AddObserver(EventHandler handler , int 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;
//}
SenderTable subTable = GetSenderTable(notificationName);
System.Object key = (sender != null) ? sender : this;
List<EventHandler> list = GetObservers(subTable , key);
if (!list.Contains(handler))
list.Add(handler);
}

有了注册,再就是对事件撤销的接口提供:

public void RemoveObserver(EventHandler handler)
{
int[] keys = new int[_table.Keys.Count];
_table.Keys.CopyTo(keys , 0);
for (int i = keys.Length - 1 ; i >= 0 ; --i)
RemoveObserver(handler , keys[i]);
}
public void RemoveObserver(EventHandler handler , int notificationName)
{
if (handler == null)
{
Debug.LogError("Can't remove a null event handler from notification");
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;
System.Object[] keys = new object[_table[notificationName].Keys.Count];
_table[notificationName].Keys.CopyTo(keys , 0);
for (int i = keys.Length - 1 ; i >= 0 ; --i)
RemoveObserver(handler , notificationName , keys[i]);
}
public void RemoveObserver(EventHandler handler , int notificationName , System.Object sender)
{
//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 = GetSenderTable(notificationName);
System.Object key = (sender != null) ? sender : this;
if (!subTable.ContainsKey(key))
return;
List<EventHandler> list = GetObservers(subTable , key);
for (int i = list.Count - 1 ; i >= 0 ; --i)
{
if (list[i] == handler)
{
list.RemoveAt(i);
break;
}
}
if (list.Count == 0)
{
subTable.Remove(key);
if (subTable.Count == 0)
_table.Remove(notificationName);
}
}

最后是对消息发送的接口:

public void PostNotification(int notificationName)
{
PostNotification(notificationName , null);
}
public void PostNotification(int notificationName , System.Object sender)
{
PostNotification(notificationName , sender , EventArgs.Empty);
}
public void PostNotification(int notificationName , System.Object sender , EventArgs e)
{
//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;
// Post to subscribers who specified a sender to observe
SenderTable subTable = GetSenderTable(notificationName);
if (sender != null && subTable.ContainsKey(sender))
{
List<EventHandler> handlers = GetObservers(subTable , sender);
for (int i = handlers.Count - 1 ; i >= 0 ; --i)
handlers[i](sender , e);
}
// Post to subscribers who did not specify a sender to observe
if (subTable.ContainsKey(this))
{
List<EventHandler> handlers = GetObservers(subTable , this);
for (int i = handlers.Count - 1 ; i >= 0 ; --i)
handlers[i](sender , e);
}
}
为了减少各个类间的耦合性,可以将他进一步封装,这样在各个类里可以直接使用而不用实例化东西,既保持了代码的简洁性,对于查找引用也很方便,接口集中化,修改和移除时也很方便:

public static class ObjectExtensions
{
#region 事件 注册 、注销
public static void PostNotification(this object obj , int notificationName)
{
NotificationCenter.instance.PostNotification(notificationName , obj);
}
public static void PostNotification(this object obj , int notificationName , EventArgs e)
{
NotificationCenter.instance.PostNotification(notificationName , obj , e);
}
public static void AddObserver(this object obj , EventHandler handler , int notificationName)
{
NotificationCenter.instance.AddObserver(handler , notificationName);
}
public static void AddObserver(this object obj , EventHandler handler , int notificationName , object sender)
{
NotificationCenter.instance.AddObserver(handler , notificationName , sender);
}
public static void RemoveObserver(this object obj , EventHandler handler)
{
NotificationCenter.instance.RemoveObserver(handler);
}
public static void RemoveObserver(this object obj , EventHandler handler , int notificationName)
{
NotificationCenter.instance.RemoveObserver(handler , notificationName);
}
public static void RemoveObserver(this object obj , EventHandler handler , int notificationName , System.Object sender)
{
NotificationCenter.instance.RemoveObserver(handler , notificationName , sender);
}
#endregion
#region 获取Unicode
#endregion
}
public class EventArgsUs:EventArgs
{
public object param1;
public object param2;
public object param3;
public EventArgsUs(object param1,object param2,object param3=null)
{
this.param1 = param1;
this.param2 = param2;
this.param3 = param3;
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值