[讀書筆記]|管理器的啓動與終止

每個遊戲都會包含一些底層支持系統, 例如資源管理器、日誌系統、對象池、動畫管理器、音樂/音效管理器等。這些管理器通常是一個單例類,如下面這段代碼:

class ResourceManager
{
private:
    static ResourceManager* m_pInstance;
public:
    static ResourceManager* get()
    {
        if(m_pInstance == nullptr)
        {
            m_pInstance = new ResourceManager();
        }
        return m_pInstance;
    }
};

我所在的項目之前就是使用這種方式實現的。
然而,這種方式有個極大的弊端,就是不能控制其構造和析構的時間。假如A管理器依賴B管理器,而在B管理器創建之前A已經被調用,或者B管理器已經被析構,而A還存在, 很有可能導致嚴重問題。
事實上這個實現方式確實導致過我們遊戲崩潰。
於是上述設計被改成如下方案:

class ResourceManager
{
private:
    static ResourceManager* m_pInstance;
public:
    static ResourceManager* get()
    {
        return m_pInstance;
    }
    static void createInstance()
    {
        m_pInstance = new ResourceManager();
    }
    static void destoryInstance()
    {
        delete m_pInstance;
    }
};
//其他管理器
...
//
class GOW
{
    static void Init()
    {
        ResourceManager::createInstance();
        //其他管理器同上
        ...
        //
    }
    static void UnInit()
    {
        ResourceManager::destoryInstance();
        //其他管理器同上
        ...
        //
    }
};

即明確的爲各單例管理器定義構造和析構的函數,替代簡單的get,這樣就可以按所需的明確次序調用各啓動和終止函數。
書中提到了更優雅的方法是:啓動時將這些管理器按所需次序啓動並放入一個棧中,這樣終止時,可以逐一把管理器彈出棧並調用終止函數。根據這個思路,我想到如下實現:

class IManager
{
    static void startUp() = 0;//啓動管理器
    static void shutDown() = 0;//終止管理器
};

class ResourceManager: public IMananger
{
private:
    static ResourceManager* m_pInstance;
    ResourceManager()
    {
        //不做任何事
    }
    ~ResourceManager()
    {
        //不做任何事
    }
public:
    static ResourceManager* get()
    {
        if(m_pInstance == nullptr)
        {
            m_pInstance = new ResourceManager();
        }
        return m_pInstance;
    }
    static void startUp() override
    {
        //管理器的啓動代碼
    }
    static void shutDown() override
    {
        //管理器的終止代碼
    }
};
//其他管理器
...
//
class GOW
{
private:
    static std::stack<IManager*> m_stkManager;
    //這裏使用棧的原因是,一般先啓動的管理器更爲基礎,所以要後終止
    static void InitManager(IManager* pMananger)
    {
        pManager->startUp();
        m_stkManager.push(pMananger);
    }
public:
    static void Init()
    {
        InitManager(ResourceManager::get());
        //其他管理器同上
        ...
        //
    }
    static void UnInit()
    {
        while(!m_stkManager.empty())
        {
            auto* pManager = m_stkManager.top();
            m_stkManager.pop();
            pManager->shutDown();
            delete pManager;
        }
    }
};

這種方式有個好處,在新增或者修改導致一些管理器的依賴順序改變時,只需要修改Init函數中的啓動順序,則終止的順序也會跟着改變。引擎開發者只需要關注管理器的啓動順序就好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章