[Unity 程式碼寫法整理]訊息機制(三)

老子敲程式碼就是一把梭發表於2019-03-02

實現第二種即時的訊息機制,寫法的話跟第一種幾乎一樣,只是去掉了Queue並且在訊息發出時執行Handler

==============================

1.訊息體 AbstractMessage:

  public abstract class AbstractMessage
  {
      public string messageName;
  }

同第一種

 ==============================

2.訊息處理函式Handler:

Action<AbstractMessage> handler

同第一種

 ==============================

3.訊息中心MessageCenter在第一種的基礎上做一些修改

public class MessageCenter : MonoSingleton<MessageCenter>
{
    private Dictionary<string,Action<AbstractMessage>> messageDic = new Dictionary<string,Action<AbstratMessage>>();
 
    //HandleMessage 處理髮送的訊息
    private void HandleMessage(AbstractMessage message)
    {
        string messageName = message.messageName;
        if(messageDic.ContainsKey(messageName))
        {
            messageDic[messageName](message);
            return;
        }
        Debug.LogError("This message has not registed : " + message.messageName);
    }
 
    //RegistMessage 註冊訊息
    public void RegistMessage(string messageName,Action<AbstractMessage> handler)
    {
        if(messageDic.ContainsKey(messageName))
        {
            messageDic[messageName] += handler;
            return;
        }
        messageDic[messageName] = handler;
    }    
 
    //UnRegistMessage 登出訊息
    public void UnRegistMessage(string messageName,Action<AbstractMessage> handler)
    {
        if(messageDic.ContainsKey(messageName))
        {
            if(messageDic[messageName].GetInvocationList().Length > 1)
            {
                messageDic[messageName] -= handler;
                return;
            }
            messageDic.Remove(messageName);
            return;
        }
        Debug.LogError("This Message Not Registed : " + messageName);
    }
 
    //DispatchMessage 發訊息
    public void DispatchMessage(AbstractMessage message)
    {
        HandleMessage(message);
    }
}

相當於是第一種訊息中心的簡化,MessageQueue理論上來說,並不需要單例類繼承Monobehaviour了,因為不需要Update去實現Loop檢測訊息功能。但是考慮到可以將兩種訊息機制合二為一,所以還是繼承MonoSingleton。

 ==============================

4.在對訊息機制的實際運用中,只有三種方法需要運用到,也就是MessageCenter裡的註冊、登出、傳送訊息。而兩種訊息機制除了傳送訊息,其他並沒有區別,也就是說,可以給傳送訊息再新增一個引數,以區分這兩種訊息種類,我比較偏向於使用列舉來實現。

public enum DispatchMessageType
{
    Immediate,    //傳送即時訊息
    Queued    //傳送佇列訊息
}


public class MessageCenter : MonoSingleton<MessageCenter>
{
    private Dictionary<string,Action<AbstractMessage>> messageDic = new Dictionary<string,Action<AbstratMessage>>();
 
    private Queue<AbstractMessage> messageQueue = new Queue<AbstractMessage>();
 
    //Call per frame
    private void Update()
    {
        while(messageQueue.Peek() != null)
        {
            AbstractMessage message = messageQueue.Dequeue();
            HandleMessage(message);
        }
    }
 
    //HandleMessage 處理髮送的訊息
    private void HandleMessage(AbstractMessage message)
    {
        string messageName = message.messageName;
        if(messageDic.ContainsKey(messageName))
        {
            messageDic[messageName](message);
            return;
        }
        Debug.LogError("This message has not registed : " + message.messageName);
    }
 
    //RegistMessage 註冊訊息
    public void RegistMessage(string messageName,Action<AbstractMessage> handler)
    {
        if(messageDic.ContainsKey(messageName))
        {
            messageDic[messageName] += handler;
            return;
        }
        messageDic[messageName] = handler;
    }    
 
    //UnRegistMessage 登出訊息
    public void UnRegistMessage(string messageName,Action<AbstractMessage> handler)
    {
        if(messageDic.ContainsKey(messageName))
        {
            if(messageDic[messageName].GetInvocationList().Length > 1)
            {
                messageDic[messageName] -= handler;
                return;
            }
            messageDic.Remove(messageName);
            return;
        }
        Debug.LogError("This Message Not Registed : " + messageName);
    }
 
    //DispatchMessage 發訊息
    public void DispatchMessage(AbstractMessage message,DispatchMessageType typeEnum)
    {
        switch(typeEnum)
        {
            case DispatchMessageType.Immediate: 
                HandleMessage(message);
                break;
            case DispatchMessageType.Queued:
                messageQueue.Enqueue(message);
                break;
            default:break;
        }
    }
}

這樣就將即時訊息和佇列訊息合二為一,組成了一個完整的訊息中心。

 ==============================

5.最後補充一下,因為現在所使用的Handler都是委託型別,如果將其改為介面,則可以在一個類中實現介面中的函式配合switch以達成統一處理訊息的目的。比如定義一個介面IMessageHandler。

public interface IMessageHandler
{
    void OnReceiveMessage(AbstractMessage message);
}

然後將MessageCenter裡面所有的Action<AbstractMessage>全部替換為IMessageHandler(懶得上程式碼)

這樣的話,假如一個類需要接收訊息,繼承此介面即可

public class Test : Monobehaviour,IHandleMessage
{
    public void OnReceiveMessage(AbstractMessage message)
    {
        switch(message.messageName)
        {
            case "message1":
                //do something
                break;
            //case ...
        }
    }
}

或者再封裝一層Monobehaviour,提供註冊和登出函式。

public abstract MessageMonobehaviour : MonoBehaviour,IHandler
{
    protected virtual void RegistMessage(string MessageName)
    {
        //由於委託替換為介面,直接用this即可
        MessageCenter.Instance.RegistMessage(MessageName,this);
    }

    protected virtual void UnRegistMessage(string MessageName)
    {
        //由於委託替換為介面,直接用this即可
        MessageCenter.Instance.UnRegistMessage(MessageName,this);
    }

    //子類重寫此方法以處理訊息
    public virtual void OnReceiveMessage(AbstractMessage message)
    {
        Debug.Log(this.GetType() + " ReceiveMessage, MessageName : " + message.messageName);
    }
}

 ==============================

沒了,訊息機制應該就這麼多,想到了再補充

相關文章