[C#/Unity] 更多 Singleton – More Singleton in Unity

在舊文章 應用 Singleton pattern 及 Unity Component 做系統拆分與管理 – Dividing your game system in unity. 中,我開發了一套架構來作為單一 Singleton 的替代方案,用於統一管理會在整個 Unity 專案中使用到的遊戲功能系統。

該篇文章中的 GameSystemMono (即仿 Singlton 的組件) 繼承了 MonoBehaviour,來實現一些設計上的想法。不過帶來優點的同時也產生了一些限制,經過與他人的討論後,認為還是需要一個不依賴 MonoBehaviour 的 Singleton 組件方案,兩者互相補足,而這個想法終於在最近進行了實作。

做為 GameModule 基底的介面 (interface)

上一個 Singleton 系統將組件命名 GameSystemMono;而這次的組件我則命名為 GameModule,下文也會如此稱呼與區分。

而 GameModule 的採用介面 (interface) 來實現,其中 IModular 是必備的,IModuleUpdatable 是有 Update 需求時可以增加的介面。目前只開發了兩個介面,未來如有需要,還能繼續擴充。

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

namespace DouduckGame {
    public interface IModular {
        void ModuleInitialize ();
        void ModuleUninitialize ();
    }
    public interface IModuleUpdatable {
        void ModuleUpdate ();
    }
}

採用 介面(interface) 而非 抽象類別(abstract class) 的原因:

  • 因為並沒有繼承 MonoBehaviour,因此並沒有寫為類別的需要。
  • C# 不支援多重繼承 (Multiple Inheritance),所以如果採用類別,則會降低組件繼承上的靈活性。
  • 介面可以支援多重組合,而且是可以在繼承之外另外加上的,相當於可以將多種類別都轉換成 GameModule 來使用而不受限。

GameModule 的管理器

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

namespace DouduckGame {
    public class GameModuleManager {

        private Dictionary<Type, IModular> m_GameModuleList;
        private List<IModuleUpdatable> m_GameModuleUpdateList;

        public GameModuleManager () {
            m_GameModuleList = new Dictionary<Type, IModular> ();
            m_GameModuleUpdateList = new List<IModuleUpdatable> ();
        }

        public void UpdateModule () {
            for (int i = m_GameModuleUpdateList.Count - 1; i >= 0; i--) {
                m_GameModuleUpdateList[i].ModuleUpdate ();
            }
        }

        public T AddModule<T>() where T : class, IModular, new() {
            if (m_GameModuleList.ContainsKey (typeof (T))) {
                Util.UnityConsole.LogError ("[GameModuleManager] There was a " + typeof (T).Name);
                return null;
            } else {
                T newModule_ = new T();
                newModule_.ModuleInitialize ();
                m_GameModuleList.Add (newModule_.GetType (), newModule_);
                if (newModule_ is IModuleUpdatable) {
                    m_GameModuleUpdateList.Add (newModule_ as IModuleUpdatable);
                }
                return newModule_;
            }
        }

        public void RemoveModule<T>() where T : class, IModular {
            if (m_GameModuleList.ContainsKey (typeof (T))) {
                m_GameModuleList[typeof (T)].ModuleUninitialize ();
                if (m_GameModuleList[typeof (T)] is IModuleUpdatable) {
                    m_GameModuleUpdateList.Remove (m_GameModuleList[typeof (T)] as IModuleUpdatable);
                }
                m_GameModuleList.Remove (typeof (T));

            } else {
                Util.UnityConsole.LogError ("[GameModuleManager] There was no " + typeof (T).Name);
            }
        }

        public T GetModule<T>() where T : class, IModular {
            if (m_GameModuleList.ContainsKey (typeof (T))) {
                return m_GameModuleList[typeof (T)] as T;
            } else {
                Util.UnityConsole.LogError ("[GameModuleManager] There was no " + typeof (T).Name);
                return null;
            }
        }
    }
}

管理器的實作方式與 GameSystemMono 的管理器大同小異。

在 AddModule() 的時候,必須利用 is 運算子檢查是否有額外的介面被新的 GameModule 所使用。比如發現了 IModuleUpdatable 介面,則要做而外的處理。

對應不同介面,可能也會有特別的 public 方法用於觸發或操作,對於 IModuleUpdatable 介面,我準備了一個 UpdateModule () 來進行觸發。

GameSystemMono 與 GameModule 的比較

GameSystemMono 的實作採用繼承 MonoBehaviour,優勢是可以在 Inspector 視窗針對每個組件系統進行參數的設定,並且可以直接使用 Update 等 Unity Message Event 進行動作;缺點則是一定要有個 GameObject 進行載體,而且採用類別繼承的設計,會導致一些彈性上的限制,不需要 MonoBehaviour 功能的組件也會被迫繼承相關特性。

GameModule 的實作採用介面 (interface),優勢是可以跟繼承關係並存,切依照需求選擇需要的而外功能介面進行擴充,彈性較高;缺點則是所有的初始化工作就都必須撰寫腳本來完成,而且沒有可以在 Inspector 調整追蹤參數的手段。

就上述的筆記可以看出來,GameSystemMonoGameModule 其實是相互補的兩個 Singleton 註冊架構,可以在一個專案中同時採用,依照組件的需求吉特,來決定要包裝成 GameSystemMono 或是 GameModule 來管理。

另外還有單一獨立的 Singleton 實作 (如參考所附),如果組件沒有與其他組件 互動 的需求,只是單單最為一個 被呼叫 的子系統,也可以採用做為一個簡單易用的方法。

單例模式 (Singleton pattern) 是一個相對有較多爭議的設計模式,為了避免在使用的時候會將 Singleton 的缺點凸顯出來,更多的設計考量與深入了解是難免要面對的。

參考

Advertisements

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s