UF_OLDL3——事件中心模块
事件中心
事件中心是基于观察者设计模式来设计的,目的是降低程序耦合性,减少程序复杂度,会使用委托,字典
为何要用事件中心
在以前,没有事件中心的时候,
面对一个对象被修改时其它对象要做自动更新的问题:
例如一个怪物死亡,需要让其它对象例如玩家执行什么方法,
我们可能就会让怪物关联这些对象,然后在死亡的方法里调用这些对象
但是,这种方法让几个不相关的类之间的耦合度增加了,修改其中一个类,就有可能影响另几个类,这显然不利于我们程序后续的编写和维护
基于观察者设计模式来设计的事件中心可以解决这种一个对象状态改变给其他对象通知的问题
观察者设计模式
观察者模式 | 菜鸟教程 (runoob.com)
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。
比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决: 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用: 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
事件中心的思路
还是假设我们需要一个怪物对象死亡后,玩家,任务记录,和其它需要执行相对应的方法
假设我们已经有了一个事件中心
- 玩家,任务记录,和其它这三个对象,它们需要提前告知事件中心:我们要监听 怪物死亡事件,
- 之后怪物对象死亡,怪物将怪物死亡事件告诉给事件中心,
- 事件中心接收到怪物死亡事件后,发现玩家,任务记录,和其它这三个对象要在这件事后执行方法
- 事件中心告诉玩家,任务记录,和其它这三个对象:应该触发怪物死亡事件之后要执行的方法了
- 玩家,任务记录,和其它这三个对象执行怪物对象死亡后需要执行相对应的方法
以上是使用事件中心处理一次一个对象状态改变给其他对象通知,其它的类似的情况也可以用这种思路来进行
可以看到,使用事件中心可以避免几个毫不相关的类关联起来
对象只要向事件中心告知要监听什么事件,告知什么事件发生了,
之后就由事件中心传达监听者需要执行方法了(事件中心也不需要知道这个方法是什么)
对象和事件中心只需要传达事件和委托,而不需要知道除了自己以外的类要执行什么方法
事件中心模块
知识点:字典,委托
作用:通过一个指定的事件名,设置事件监听委托,当有对象使用该事件名触发事件时,执行该事件名对应的委托里的所有方法
使用方法:
- 添加事件监听,传入事件名和用来处理事件的委托函数(需要object类型参数,传入触发事件者的消息,用于监听者区分不同对象触发的消息)
- 移除事件监听,要注意!至少要在对象销毁生命函数里设置移除对应的事件监听,否则在监听者被销毁后,还执行其委托会造成内存泄漏等问题!
- 触发事件,触发者需要传入自己的消息(object)提供给监听者,若字典内存在该事件名,就执行该事件名对应的委托
- 清空事件中心,一般用于过场景时
注意:
添加事件监听一定要配套移除事件监听,至少要在对象销毁生命函数里设置移除对应的事件监听,否则在监听者被销毁后,还执行其委托会造成内存泄漏等问题! 这意味着,事件中心模块使用匿名函数必须慎重!因为匿名函数不能移除,只能全部清理! 要注意确认输入的事件名是否正确!以确保事件正常触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| using System.Collections.Generic; using UnityEngine.Events;
public class EventCenter : BaseManager<EventCenter> { private Dictionary<string, UnityAction<object>> eventDic = new Dictionary<string, UnityAction<object>>();
public void AddEventListener(string eventName, UnityAction<object> action) { if (eventDic.ContainsKey(eventName)) { eventDic[eventName] += action; } else { eventDic.Add(eventName, action); } }
public void EventTrigger(string eventName, object info) { if (eventDic.ContainsKey(eventName)) { eventDic[eventName].Invoke(info); } }
public void RemoveEventListener(string eventName, UnityAction<object> action) { if (eventDic.ContainsKey(eventName)) eventDic[eventName] -= action; }
public void Clear() { eventDic.Clear(); } }
|
优化装箱拆箱的事件中心模块
在原来的事件中心模块内,触发事件者要上传的所用的参数是object,这不可避免的会遇到装箱拆箱的问题(值类型和引用类型之间的问题)
因此我们需要利用泛型来解决我们的方法
由于新的泛型方法实现和原来的旧事件中心模块实现方法有冲突(两者的数据不能共通,原事件触发方法被其泛型方法屏蔽),两者难以共存,
因此在原有的基础上重写了事件中心模块,和原来的事件中心模块并不兼容!
使用方法:
- 添加泛型事件监听,传入事件名和用来处理事件的委托函数(由泛型参数决定传入触发事件者的消息参数的类型,用于监听者区分不同对象触发的消息)
- 添加无参事件监听,传入事件名和用来处理事件的委托函数(触发者不需要传入任何参数)
- 移除泛型事件监听, 传入事件名和用来处理事件的委托函数(由泛型参数决定传入触发事件者的消息参数的类型,用于监听者区分不同对象触发的消息)
- 移除无参事件监听,传入事件名和用来处理事件的委托函数
注意!至少要在对象销毁生命函数里设置移除对应的事件监听,否则在监听者被销毁后,还执行其委托会造成内存泄漏等问题
- 触发泛型事件,触发者需要传入自己的消息(类型由泛型决定)提供给监听者,若字典内存在该事件名,就执行该事件名对应的委托
- 触发无参事件,触发者不需要传入任何参数,若字典内存在该事件名,就执行该事件名对应的委托
- 清空事件中心,一般用于过场景时
注意:
- 添加事件监听一定要配套移除事件监听,至少要在对象销毁生命函数里设置移除对应的事件监听,
否则在监听者被销毁后,还执行其委托会造成内存泄漏等问题! 这意味着,事件中心模块使用匿名函数必须慎重!因为匿名函数不能移除,只能全部清理!
- 要注意确认输入的事件名是否正确!以确保事件正常触发
- 注意!一个事件所用的监听函数一定是对应唯一一个参数类型(哪怕是无参的)
或者说,对于同一事件,所有监听者和所有上传者所使用的泛型参数必须一致(无参的也必须都是无参) 否则,触发者上传事件后执行监听委托时会出现报错!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
| using System.Collections.Generic; using UnityEngine.Events;
public interface IEventInfo { }
public class EventInfo<T> : IEventInfo { public UnityAction<T> actions;
public EventInfo(UnityAction<T> action) { actions += action; } }
public class EventInfo : IEventInfo { public UnityAction actions; public EventInfo(UnityAction action) { actions += action; } }
public class EventCenter : BaseManager<EventCenter> { private Dictionary<string, IEventInfo> eventDic = new Dictionary<string, IEventInfo>();
public void AddEventListener<T>(string eventName, UnityAction<T> action) { if (eventDic.ContainsKey(eventName)) { (eventDic[eventName] as EventInfo<T>).actions += action; } else { eventDic.Add(eventName, new EventInfo<T>(action)); } }
public void AddEventListener(string eventName, UnityAction action) { if (eventDic.ContainsKey(eventName)) { (eventDic[eventName] as EventInfo).actions += action; } else { eventDic.Add(eventName, new EventInfo(action)); } }
public void EventTrigger<T>(string eventName, T info) { if (eventDic.ContainsKey(eventName)) { (eventDic[eventName] as EventInfo<T>).actions?.Invoke(info); } }
public void EventTrigger(string eventName) { if (eventDic.ContainsKey(eventName)) { (eventDic[eventName] as EventInfo).actions?.Invoke(); } }
public void RemoveEventListener<T>(string eventName, UnityAction<T> action) { if (eventDic.ContainsKey(eventName)) (eventDic[eventName] as EventInfo<T>).actions -= action; }
public void RemoveEventListener(string eventName, UnityAction action) { if (eventDic.ContainsKey(eventName)) (eventDic[eventName] as EventInfo).actions -= action; }
public void Clear() { eventDic.Clear(); }
#region 委托参数使用object的旧实现(全部废弃)
#endregion }
|