前一篇用用了觀察者模式實作事件管理器,但還是有可以改變的部分
前一篇的事件管理結構:
會發現類別會因為事件的多寡而越來越多,而多一個事件就會出現一個主題和一個以上的觀察者,而每一個事件還要去定義專屬的主題,而主題傳資訊還是以Object轉型為需要的資訊類別,多了一個轉型的動作,那有沒有方式可以修改這些問題呢?
這時候泛型就派上用場了
新版UML架構:
會發現類別瞬間減少很多,不會有一堆的中繼觀察者,也不用依照事件另外定義專屬的主題,更沒有傳資訊還需要轉型的問題
所以說,做了什麼變化呢?
其實這裡把事件從原本的enum直接變成一個一個的主題,然後觀察者就是需要觀測事件(主題)的類別本身,不需要在一個中繼觀察者,然後從原本的向主題拉訊息變成了直接推訊息,而且還不用轉型直接就是需要的訊息
更改後大致分為:
基底事件觀察者介面,struct泛型事件觀察者介面,事件管理器,各個struct泛型事件(主題)和各個實作觀察者介面的類別,加上一個靜態擴充類別
基底事件觀察者介面
基本上基底事件觀察者就是為了方便讓事件管理器進行存放,不需要被泛型框架住
interface EventListenerBase { }
struct泛型事件觀察者介面
interface EventListener<T>{ //繼承EventListenerBase介面,T就是事件主題本身,並限定為struct,struct更適合用於資訊傳遞,相對輕便
- 更新事件方法(傳遞的值就是事件主題本身)
}
事件管理器
EventManager{
- 包含事件(主題)與觀察者介面的Dictinary
- 觀察者介面註冊方法
- 觀察者介面取消註冊方法
- 泛型通知方法(限定struct,直接傳事件主題當資料)
}
各個struct泛型事件(主題)(這裡以TestEvent為例)
TestEvent{ //為struct,為事件主題本身
- 狀態字串參數(事件所需參數)
- 帶有字串參數的靜態的事件主題本身
- 靜態類別通知方法(傳入事件所需要的參數,並把得到參數的事件本身透過事件管理器通知給觀察者)
}
各個實作觀察者介面的類別(這裡以TestEventListener2為例)
TestEventListener2{ //實作EventListener<TestEvent>和EventListener<TestEvent2>觀察者介面
- 狀態字串參數(所需觀察之參數)
- 步驟整數參數(所需觀察之參數)
- OnEnable(註冊2個事件)
- 實作2個觀察者介面的更新事件方法
- OnDisable(取消2個事件的註冊)
}
靜態態擴充類別
static EventRegister{ //為了擴充interface註冊方法,Interface內方法無法實作的原故
- 註冊方法(給所有實作事件觀察者介面的類別一個內部註冊方法)
- 取消註冊方法(給所有實作事件觀察者介面的類別一個內部取消註冊方法)
}
Code:
- 基底事件觀察者介面與struct泛型事件觀察者介面
public interface EventListenerBase { }; public interface EventListener<T> : EventListenerBase where T : struct { void OnEvent(T eventType); }
- 事件管理器
public static class EventManager { private static Dictionary<Type, List<EventListenerBase>> m_SubscribersList; static EventManager() { m_SubscribersList = new Dictionary<Type, List<EventListenerBase>>(); } public static void AddListener<Event>(EventListener<Event> listener) where Event : struct { Type eventType = typeof(Event); if (!m_SubscribersList.ContainsKey(eventType)) { m_SubscribersList[eventType] = new List<EventListenerBase>(); } if (!SubscriptionExists(eventType, listener)) { m_SubscribersList[eventType].Add(listener); } } public static void RemoveListener<Event>(EventListener<Event> listener) where Event : struct { Type eventType = typeof(Event); if (!m_SubscribersList.ContainsKey(eventType)) { return; } List<EventListenerBase> subscriberList = m_SubscribersList[eventType]; for (int i = subscriberList.Count - 1; i >= 0; i--) { if (subscriberList[i] == listener) { subscriberList.Remove(subscriberList[i]); if (subscriberList.Count == 0) { m_SubscribersList.Remove(eventType); } return; } } } public static void TriggerEvent<Event>(Event newEvent) where Event : struct { List<EventListenerBase> list; if (!m_SubscribersList.TryGetValue(typeof(Event), out list)) return; for (int i = list.Count - 1; i >= 0; i--) { (list[i] as EventListener<Event>).OnEvent(newEvent); } } private static bool SubscriptionExists(Type type, EventListenerBase receiver) { List<EventListenerBase> receivers; if (!m_SubscribersList.TryGetValue(type, out receivers)) return false; bool exists = false; for (int i = receivers.Count - 1; i >= 0; i--) { if (receivers[i] == receiver) { exists = true; break; } } return exists; } }
- 各個struct泛型事件(主題)(這裡以TestEvent為例)
public struct TestEvent { public string m_State; public static TestEvent e; public static void Trigger(string state) { e.m_State = state; EventManager.TriggerEvent(e); } }
- 各個實作觀察者介面的類別(這裡以TestEventListener2為例)
public class TestEventListener2 : MonoBehaviour, EventListener<TestEvent>, EventListener<TestEvent2> { public string m_State; public int m_Step; private void OnEnable() { this.EventStartListening<TestEvent>(); this.EventStartListening<TestEvent2>(); } public void OnEvent(TestEvent eventType) { m_State = eventType.m_State; Debug.Log($"m_TestEventListener2 state : {m_State}"); } public void OnEvent(TestEvent2 eventType) { m_Step = eventType.m_Step; Debug.Log($"m_TestEventListener2 step : {m_Step}"); } private void OnDisable() { this.EventStopListening<TestEvent>(); this.EventStopListening<TestEvent2>(); Debug.Log("m_TestEventListener2 is canceled"); } }
- 靜態態擴充類別
public static class EventRegister { public static void EventStartListening<EventType>(this EventListener<EventType> caller) where EventType : struct { EventManager.AddListener<EventType>(caller); } public static void EventStopListening<EventType>(this EventListener<EventType> caller) where EventType : struct { EventManager.RemoveListener<EventType>(caller); } }
以上定義都結束後就可以做一個測試了
public class Test : MonoBehaviour { public TestEventListener m_TestEventListener; public TestEventListener2 m_TestEventListener2; public void Start() { Debug.Log($"m_TestEventListener state : {m_TestEventListener.m_State}"); Debug.Log($"m_TestEventListener2 state : {m_TestEventListener2.m_State}"); Debug.Log($"m_TestEventListener2 step : {m_TestEventListener2.m_Step}"); TestEvent.Trigger("Uptate"); TestEvent2.Trigger(1); Destroy(m_TestEventListener2); TestEvent.Trigger("Uptate2"); TestEvent2.Trigger(2); } }
最後得到的結果:
一切都變得簡單許多,只要某事件被觸發,直接找到事件本身,然後呼叫,結束!
剩下的就是串接其他觀察者的部分了,像之前說到的成就系統一樣也能直接串接了,之後有時間會在討論成就系統的部分
留言列表