技巧不意味着“奇技淫巧”,而是一种通过某些方法和手段来让开发过程更加容易,开发内容更加直观的方式。在开发过程中,多多使用技巧而不是一味的生搬硬套是值得提倡,但是若盲目追求技巧的使用而不顾代码的健壮性,那就得不偿失了。(不定期更新)
附:下面内容配套使用的软件为Unity,但技巧是通用的。
将各个功能进行解耦合开发,是从初级程序员走向中级程序员的标志。因此,深入了解解耦合的方法,能在开发过程中自然而然地使用出来,是一个优秀程序员的基本素养。
一. 使用事件消息的广播和监听
使用事件的广播和监听,需要对委托有所了解,这里推荐一篇讲C#中委托比较好的文章。同时也需要了解一下静态类。
该方法的优势也是比较明显的,广播者不需要关心监听者是谁,监听者也不需要关心广播者,每个代码只需要关心自己的事情。
首先写好消息管理类
// 定义委托类型,委托相当于定义了一个类似int的数据类型,使用函数为其赋值
public delegate void Callback();
public delegate void Callback<T>(T arg);
// 定义事件类型,监听者只对特定的事件类型进行监听
//(当然也是可以不定类型而对所有事件进行监听,但这样效率低而且很乱)
public enum EventType{
ExampleType1,
ExampleType2
}
// 定义事件消息管理中心,提供添加监听和进行广播的方法
public static EventCenter{
private static Dictionary<EventType, Delegate> m_eventDic = new Dictionary<EventType, Delegate>();
// 添加监听者的方法(对无参Callback类型的添加)
public static void AddMonitor(EventType eventType, Callback callback)
{
OnAddMonitor(eventType, callback);
m_eventDic[eventType] = (Callback)m_eventDic[eventType] + callback; // 相当于调用了Delegate类的Combine方法
}
// 添加监听者的方法(对单个泛型Callback类型的添加,后面多个的以此类推)
public static void AddMonitor<T>(EventType eventType, Callback<T> callback)
{
OnAddMonitor(eventType, callback);
m_eventDic[eventType] = (Callback<T>)m_eventDic[eventType] + callback;
}
// 移除监听者的方法(对无参Callback类型的移除)
public static void RemoveMonitor(EventType eventType, Callback callback){
OnRemoveMonitor(eventType, callback);
m_eventDic[eventType] = (Callback)m_eventDic[eventType] - callback;
OnRemoveEventType(eventType);
}
// 移除监听者的方法(对单个泛型Callback类型的移除,后面多个的以此类推)
public static void RemoveMonitor<T>(EventType eventType, Callback<T> callback){
OnRemoveMonitor(eventType, callback);
m_eventDic[eventType] = (Callback<T>)m_eventDic[eventType] - callback;
OnRemoveEventType(eventType);
}
// 广播事件消息(对无参Callback类型)
public static void Broadcast(EventType eventType)
{
Delegate temDel;
if (m_eventDic.TryGetValue(eventType, out temDel))
{
Callback callback = temDel as Callback;
if (callback != null)
callback();
else
throw new Exception(string.Format("广播事件错误:事件{0}中委托为空或具有不同类型委托;", eventType));
}
}
// 广播事件消息(对单个泛型Callback类型,后面多个的以此类推)
public static void Broadcast<T>(EventType eventType, T arg)
{
Delegate temDel;
if (m_eventDic.TryGetValue(eventType, out temDel))
{
Callback<T> callback = temDel as Callback<T>;
if (callback != null)
callback(arg);
else
throw new Exception(string.Format("广播事件错误:事件{0}中委托为空或具有不同类型委托;", eventType));
}
}
private static void OnAddMonitor(EventType eventType, Delegate callback)
{
if (!m_eventDic.ContainsKey(eventType))
m_eventDic.Add(eventType, null);
Delegate temDel = m_eventDic[eventType];
if (temDel != null && temDel.GetType() != callback.GetType())
throw new Exception(string.Format("添加监听错误:尝试为事件{0}添加委托,但当前事件委托{1}与要添加的委托类型{2}不同;", eventType, temDel.GetType(), callback.GetType());
}
private static void OnRemoveMonitor(EventType eventType, Delegate call)
{
if (m_eventDic.ContainsKey(eventType))
{
Delegate temDel = m_eventDic[eventType];
if (temDel == null)
throw new Exception(string.Format("移除监听错误:事件{0}没有对应委托;", eventType));
else if (temDel.GetType() != callback.GetType())
throw new Exception(string.Format("移除监听错误:尝试为事件{0}移除委托,但当前事件委托{1}与要移除的委托类型{2}不同;", eventType, temDel.GetType(), callback.GetType());
}
else
throw new Exception(string.Format("移除监听错误:并没有该事件码{0};", eventType));
}
private static void OnRemoveEventType(EventType eventType){
if (m_eventDic[eventType] == null)
m_eventDic.Remove(eventType);
}
}
下面的监听者只需要利用写好的消息类就可以很方便的对广播者的某个事件进行反馈,而且并不会因为缺少某个角色而报错(没有广播者或监听者并不影响)。
// 监听者
public class Monitor : MonoBehaviour{
void Awake(){
EventCenter.AddMonitor<int>(EventType.ExampleTest1, Test);
}
void OnDestroy(){
EventCenter.RemoveMonitor<int>(EventType.ExampleTest1, Test);
}
private void Test(int num){
Debug.Log("我被调用啦" + num);
}
}
// 广播者
public class Broadcaster{
void Start(){
// 这里也体现了这种方法的劣势——广播时传入的参数必须和监听者对应事件的方法的参数类型一致,
// 但这里并不能直接知道对应事件的参数类型,只能通过查找那个事件或者写说明文档来解决。
EventCenter.Broadcast(EventType.ExampleTest1, 2);
}
}