close

前一篇用用了觀察者模式實作事件管理器,但還是有可以改變的部分

前一篇的事件管理結構:

Untitled (2)

會發現類別會因為事件的多寡而越來越多,而多一個事件就會出現一個主題和一個以上的觀察者,而每一個事件還要去定義專屬的主題,而主題傳資訊還是以Object轉型為需要的資訊類別,多了一個轉型的動作,那有沒有方式可以修改這些問題呢?

這時候泛型就派上用場了

 

新版UML架構:

Untitled (1)

會發現類別瞬間減少很多,不會有一堆的中繼觀察者,也不用依照事件另外定義專屬的主題,更沒有傳資訊還需要轉型的問題

所以說,做了什麼變化呢?

其實這裡把事件從原本的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);
    }
}

最後得到的結果:

截圖 2023-01-09 下午1.49.55

一切都變得簡單許多,只要某事件被觸發,直接找到事件本身,然後呼叫,結束!

剩下的就是串接其他觀察者的部分了,像之前說到的成就系統一樣也能直接串接了,之後有時間會在討論成就系統的部分

arrow
arrow

    Kouhei 發表在 痞客邦 留言(0) 人氣()