[读书笔记]|管理器的启动与终止

每个游戏都会包含一些底层支持系统, 例如资源管理器、日志系统、对象池、动画管理器、音乐/音效管理器等。这些管理器通常是一个单例类,如下面这段代码:

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函数中的启动顺序,则终止的顺序也会跟着改变。引擎开发者只需要关注管理器的启动顺序就好。

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