苦难是人生的老师,通过苦难,走向欢乐! —— 贝多芬 开始学习
我们知道通过在Unity3D中通过GetComponent就可以获得某个模块的实例,进而引用这个实例完成相关任务的调用。可是显然这种方法,就像我们随身带着现金去和不同的人进行交易,每次交易的时候都需要我们考虑现金的支入和支出问题,从安全性和耦合度两个方面进行考虑,这种方法在面对复杂的系统设计的时候,非常容易造成模块间的相互依赖,即会增加不同模块间的耦合度。为了解决这个问题,大家开始考虑单例模式,因为单例模式能够保证在全局内有一个唯一的实例,所以这种方式可以有效地降低模块间的直接引用。单例模式就像是我们在银行内办理了一个唯一的账户,这样我们在交易的时候只需要通过这个账户来进行控制资金的流向就可以了。单例模式确保了各个模块间的独立性,可是单例模式更多的是一种主动行为,即我们在需要的时候主动去调用这个模块,单例模式存在的问题是无法解决被调用方的反馈问题,除非被调用方主动地去调用调用方的模块实例。说到这里我们好像看到了一种新的模式,这就是我们下面要提到的事件机制。
订阅者模式和事件机制
首先这里要提到一种称为“订阅者模式”的设计模式,这种设计模式在《大话设计模式》这本书中称为“观察者模式”或者“发布-订阅(Publish/Subscribe)模式”,我们这里暂且叫做“订阅者模式”吧!该模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个对象在状态发生变化时会通知所有观察者对象,使它们能够自动更新自己。针对这个模式,我们可以考虑事件机制的实现,事件机制可以理解为在一个事件中心(Subject)保存有对所有事件(Observer)的引用,事件中心负责对这些事件进行分发,这样每个事件就可以通过回调函数的方式进行更新,这样就实现了一个事件机制。下面给出基本的代码实现:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UniEventDispatcher
{
///
/// 定义事件分发委托
///
public delegate void OnNotification(Notification notific);
///
///通知中心
///
public class NotificationCenter
{
///
/// 通知中心单例
///
private static NotificationCenter instance=null;
public static NotificationCenter Get()
{
if(instance == null){
instance = new NotificationCenter();
return instance;
}
return instance;
}
///
/// 存储事件的字典
///
private Dictionary eventListeners
= new Dictionary();
///
/// 注册事件
///
/// 事件Key
/// 事件监听器
public void AddEventListener(string eventKey,OnNotification eventListener)
{
if(!eventListeners.ContainsKey(eventKey)){
eventListeners.Add(eventKey,eventListener);
}
}
///
/// 移除事件
///
/// 事件Key
public void RemoveEventListener(string eventKey)
{
if(!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey] =null;
eventListeners.Remove(eventKey);
}
///
/// 分发事件
///
/// 事件Key
/// 通知
public void DispatchEvent(string eventKey,Notification notific)
{
if (!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey](notific);
}
///
/// 分发事件
///
/// 事件Key
/// 发送者
/// 通知内容
public void DispatchEvent(string eventKey, GameObject sender, object param)
{
if(!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey](new Notification(sender,param));
}
///
/// 分发事件
///
/// 事件Key
/// 通知内容
public void DispatchEvent(string eventKey,object param)
{
if(!eventListeners.ContainsKey(eventKey))
return;
eventListeners[eventKey](new Notification(param));
}
///
/// 是否存在指定事件的监听器
///
public Boolean HasEventListener(string eventKey)
{
return eventListeners.ContainsKey(eventKey);
}
}
}
注意到在这个“通知中心”中,我们首先实现了单例模式,这样我们可以通过Get方法来获取该“通知中心”的唯一实例,其次这里利用一个字典来存储对所有事件的引用,这样保证外部可以通过AddEventListener和RemoveEventListener这两个方法来进行事件的添加和移除,对于添加的事件引用我们可以通过DispatchEvent方法来分发一个事件,事件的回调函数采用委托来实现,注意到这个委托需要一个Notification类型,对该类型简单定义如下:
using System;
using UnityEngine;
namespace UniEventDispatcher
{
public class Notification
{
///
/// 通知发送者
///
public GameObject sender;
///
/// 通知内容
/// 备注:在发送消息时需要装箱、解析消息时需要拆箱
/// 所以这是一个糟糕的设计,需要注意。
///
public object param;
///
/// 构造函数
///
/// 通知发送者
/// 通知内容
public Notification(GameObject sender, object param)
{
this.sender = sender;
this.param = param;
}
///
/// 构造函数
///
///
public Notification(object param)
{
this.sender = null;
this.param = param;
}
///
/// 实现ToString方法
///
///
public override string ToString()
{
return string.Format("sender={0},param={1}", this.sender, this.param);
}
}
}
对Notification的定义需要提供发送者和发送内容,这样可以保证所有的通知都按照这样的格式进行定义,如果有Socket开发经验的朋友可能会联想到通讯协议的定义,这里是比较相似啦,哈哈!
使用事件机制的一个示例