1. 程式人生 > >觀察者模式(基於CSharpMessenger擴充套件)

觀察者模式(基於CSharpMessenger擴充套件)

在使用c#中的事件時,經同事推薦看到了這個框架,它是c#訊息傳遞的高階版本,程式碼不多,拜讀之後對程式碼結構進行了重構,感覺更清晰點

/*
 * Advanced C# messenger by Ilya Suzdalnitski. V1.0
 * 
 * Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended".
 * 
 * Features:
 	* Prevents a MissingReferenceException because of a reference to a destroyed message handler.
 	* Option to log all messages
 	* Extensive error detection, preventing silent bugs
 * 
 * Usage examples:
 	1. Messenger.AddListener<GameObject>("prop collected", PropCollected);
 	   Messenger.Broadcast<GameObject>("prop collected", prop);
 	2. Messenger.AddListener<float>("speed changed", SpeedChanged);
 	   Messenger.Broadcast<float>("speed changed", 0.5f);
 * 
 * Messenger cleans up its evenTable automatically upon loading of a new level.
 * 
 * Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string)
 * 
 */
//巨集定不許刪除,為以後測試方便
//#define LOG_ALL_MESSAGES
//#define LOG_ADD_LISTENER
//#define LOG_BROADCAST_MESSAGE
#define REQUIRE_LISTENER

using System;
using System.Collections.Generic;
using UnityEngine;

namespace GG
{
    public class Messenger
    {
        public static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
        //Message handlers that should never be removed, regardless of calling Cleanup
        public static List<string> permanentMessages = new List<string>();

        #region Helper methods
        //Marks a certain message as permanent.
        public static void MarkAsPermanent(string eventType)
        {
#if LOG_ALL_MESSAGES
		Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\"");
#endif
            permanentMessages.Add(eventType);
        }


        public static void Cleanup()
        {
#if LOG_ALL_MESSAGES
		Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");
#endif

            List<string> messagesToRemove = new List<string>();

            foreach (KeyValuePair<string, Delegate> pair in eventTable)
            {
                bool wasFound = false;

                foreach (string message in permanentMessages)
                {
                    if (pair.Key == message)
                    {
                        wasFound = true;
                        break;
                    }
                }

                if (!wasFound)
                    messagesToRemove.Add(pair.Key);
            }

            foreach (string message in messagesToRemove)
            {
                eventTable.Remove(message);
            }
        }

        public static void PrintEventTable()
        {
            Debug.Log("\t\t\t=== MESSENGER PrintEventTable ===");

            foreach (KeyValuePair<string, Delegate> pair in eventTable)
            {
                Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value);
            }

            Debug.Log("\n");
        }
        #endregion

        #region Message logging and exception throwing
        public static void OnListenerAdding(string eventType, Delegate listenerBeingAdded)
        {
#if LOG_ALL_MESSAGES || LOG_ADD_LISTENER
		Debug.Log("MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}");
#endif

            if (!eventTable.ContainsKey(eventType))
            {
                eventTable.Add(eventType, null);
            }

            Delegate d = eventTable[eventType];
            if (d != null && d.GetType() != listenerBeingAdded.GetType())
            {
                throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
            }
        }

        public static void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved)
        {
#if LOG_ALL_MESSAGES
		Debug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}");
#endif

            if (eventTable.ContainsKey(eventType))
            {
                Delegate d = eventTable[eventType];

                if (d == null)
                {
                    throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));
                }
                else if (d.GetType() != listenerBeingRemoved.GetType())
                {
                    throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
                }
            }
            else
            {
                throw new ListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));
            }
        }

        public static void OnListenerRemoved(string eventType)
        {
            if (eventTable[eventType] == null)
            {
                eventTable.Remove(eventType);
            }
        }

        public static void OnBroadcasting(string eventType)
        {
#if REQUIRE_LISTENER
            if (!eventTable.ContainsKey(eventType))
            {
                throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType));
            }
#endif
        }

        public static BroadcastException CreateBroadcastSignatureException(string eventType)
        {
            return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
        }

        public class BroadcastException : Exception
        {
            public BroadcastException(string msg)
                : base(msg)
            {
            }
        }

        public class ListenerException : Exception
        {
            public ListenerException(string msg)
                : base(msg)
            {
            }
        }
        #endregion
    }

    #region No parameters
    public class MessengerMgr : Messenger
    {
        public static void AddListener(string eventType, Callback handler)
        {
            OnListenerAdding(eventType, handler);
            var temp = (Callback)eventTable[eventType];
            temp += handler;
            eventTable[eventType] = (Callback)eventTable[eventType] + handler;
        }
        public static void RemoveListener(string eventType, Callback handler)
        {
            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Callback)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public static void Broadcast(string eventType)
        {
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
		Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");
#endif
            OnBroadcasting(eventType);

            Delegate d;
            if (eventTable.TryGetValue(eventType, out d))
            {
                Callback callback = d as Callback;

                if (callback != null)
                {
                    callback();
                }
                else
                {
                    throw CreateBroadcastSignatureException(eventType);
                }
            }
        }
    }
    #endregion


    #region Single parameters
    public class MessengerMgr<T> : Messenger
    {
        public static void AddListener(string eventType, Callback<T> handler)
        {
            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
        }

        public static void RemoveListener(string eventType, Callback<T> handler)
        {
            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public static void Broadcast(string eventType, T arg1)
        {
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
		Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");
#endif
            OnBroadcasting(eventType);

            Delegate d;
            if (eventTable.TryGetValue(eventType, out d))
            {
                Callback<T> callback = d as Callback<T>;

                if (callback != null)
                {
                    callback(arg1);
                }
                else
                {
                    throw CreateBroadcastSignatureException(eventType);
                }
            }
        }
    }
    #endregion

    #region Two parameters
    public class MessengerMgr<T, U> : Messenger
    {
        public static void AddListener(string eventType, Callback<T, U> handler)
        {
            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler;
        }

        public static void RemoveListener(string eventType, Callback<T, U> handler)
        {
            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public static void Broadcast(string eventType, T arg1, U arg2)
        {
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
		Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");
#endif
            OnBroadcasting(eventType);

            Delegate d;
            if (eventTable.TryGetValue(eventType, out d))
            {
                Callback<T, U> callback = d as Callback<T, U>;

                if (callback != null)
                {
                    callback(arg1, arg2);
                }
                else
                {
                    throw CreateBroadcastSignatureException(eventType);
                }
            }
        }
    }
    #endregion

    #region Three parameters
    public class MessengerMgr<T, U, V> : Messenger
    {
        public static void AddListener(string eventType, Callback<T, U, V> handler)
        {
            OnListenerAdding(eventType, handler);
            eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler;
        }

        public static void RemoveListener(string eventType, Callback<T, U, V> handler)
        {
            OnListenerRemoving(eventType, handler);
            eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler;
            OnListenerRemoved(eventType);
        }

        public static void Broadcast(string eventType, T arg1, U arg2, V arg3)
        {
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
		Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\"");
#endif
            OnBroadcasting(eventType);

            Delegate d;
            if (eventTable.TryGetValue(eventType, out d))
            {
                Callback<T, U, V> callback = d as Callback<T, U, V>;

                if (callback != null)
                {
                    callback(arg1, arg2, arg3);
                }
                else
                {
                    throw CreateBroadcastSignatureException(eventType);
                }
            }
        }
    }
    #endregion
}

委託型別定義:


/** 
* 名稱空間: namespace GG 
* 功 能: 訊息回撥函式型別
* 類 名: 無 
* 建立人:zxh
* 建立時間:2018/11/23 13:42:52
* Copyright (c) 2018 gg Corporation. All rights reserved.
*/

namespace GG
{
    public delegate void Callback();
    public delegate void Callback<T>(T arg1);
    public delegate void Callback<T, U>(T arg1, U arg2);
    public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);
}

包裝層:

純屬個人編碼習慣,包一層儘量做到使用和實現分離

/** 
* 名稱空間: namespace GG 
* 功 能: 訊息包裝層
* 類 名: EventManager 
* 建立人:zxh
* 建立時間:2018/11/23 13:42:52
* Copyright (c) 2018 gg Corporation. All rights reserved.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XLua;

namespace GG
{
    [LuaCallCSharp]
    public class EventManager
    {
        public static void AddEventListener(string eventName, Callback callback)
        {
            MessengerMgr.AddListener(eventName, callback);
        }

        public static void AddEventListener<T>(string eventName, Callback<T> callback)
        {
            MessengerMgr<T>.AddListener(eventName, callback);
        }

        public static void AddEventListener<T, U>(string eventName, Callback<T, U> callback)
        {
            MessengerMgr<T, U>.AddListener(eventName, callback);
        }

        public static void AddEventListener<T, U, V>(string eventName, Callback<T, U, V> callback)
        {
            MessengerMgr<T, U, V>.AddListener(eventName, callback);
        }

        public static void RemoVeEventListener(string eventName, Callback callback)
        {
            MessengerMgr.RemoveListener(eventName, callback);
        }

        public static void RemoVeEventListener<T>(string eventName, Callback<T> callback)
        {
            MessengerMgr<T>.RemoveListener(eventName, callback);
        }

        public static void RemoVeEventListener<T, U>(string eventName, Callback<T, U> callback)
        {
            MessengerMgr<T, U>.RemoveListener(eventName, callback);
        }

        public static void RemoVeEventListener<T, U, V>(string eventName, Callback<T, U, V> callback)
        {
            MessengerMgr<T, U, V>.RemoveListener(eventName, callback);
        }

        public static void DispatchEvent(string eventName)
        {
            MessengerMgr.Broadcast(eventName);
        }

        public static void DispatchEvent<T>(string eventName, T arg1)
        {
            MessengerMgr<T>.Broadcast(eventName, arg1);
        }

        public static void DispatchEvent<T, U>(string eventName, T arg1 , U arg2)
        {
            MessengerMgr<T, U>.Broadcast(eventName, arg1, arg2);
        }

        public static void DispatchEvent<T, U, V>(string eventName, T arg1, U arg2, V arg3)
        {
            MessengerMgr<T, U, V>.Broadcast(eventName, arg1, arg2, arg3);
        }
    }
}

原始碼共有兩個檔案,加上我包裝的一層有三個檔案,程式碼結構很清晰,使用也很簡單,就是一個觀察者模式的實現,相信對c#有了解的開發人員都能看懂,