单例管理器class,更好的设计?

A singleton-like manager class, better design?

我正在制作一个游戏引擎,我正在使用库来完成各种任务。例如,我使用需要初始化的 FreeType,获取管理器,在我不使用它之后,我必须取消初始化它。当然只能初始化一次,已经初始化过才能反初始化。

我想出了什么(只是一个例子,不是 "real" 代码 [但可能是有效的 C++ 代码]):

class FreeTypeManager
{
private:
    FreeTypeManager() {} // Can't be instantiated

    static bool initialized;
    static TF_Module * module;  // I know, I have to declare this in a separate .cpp file and I do

public:
    static void Initialize()
    {
        if (initialized) return;

        initialized = true;
        FT_Initialize();
        FT_CreateModule(module);
    }

    static void Deinitialize()
    {
        if (!initialized) return;

        initialized = false;
        FT_DestroyModule(module);
        FT_Deinit();
    }
};

对于我创建的每个管理器(FreeType、AudioManager、EngineCore、DisplayManager),它几乎是一样的:没有实例,只有静态的东西。我可以看出每次都重写这个框架可能是一个糟糕的设计实践。也许有更好的解决方案。

改用单例会好吗?或者是否有适合我的问题的模式?

如果你仍然想要单例方法(这对管理器类型的对象有意义),那么为什么不让它成为一个适当的单例,并有一个静态的get 函数,如果需要,创建管理器对象,并让管理器(私有)构造函数处理初始化并在析构函数中处理取消初始化(尽管管理器类型的对象通常具有整个程序的生命周期,因此析构函数只会在程序退出时调用)。

类似

class FreeTypeManager
{
public:
    static FreeTypeManager& get()
    {
        static FreeTypeManager manager;
        return manager;
    }

    // Other public functions needed by the manager, to load fonts etc.
    // Of course non-static

    ~FreeTypeManager()
    {
        // Whatever cleanup is needed
    }

private:
    FreeTypeManager()
    {
        // Whatever initialization is needed
    }

    // Whatever private functions and variables are needed
};

如果您不想要单例,并且 class 中只有静态函数,您不妨使用 namespace。对于变量,将它们放在实现(源)文件中的匿名命名空间中。或者对数据使用不透明结构指针(pimpl 惯用法 的变体)。

还有另一种解决方案,它不完全是单例模式,但非常相关。

class FreeTypeManager
{
public:
   FreeTypeManager();
   ~FreeTypeManager();
 };
 class SomeOtherClass
 {
 public:
   SomeOtherClass(FreeTypeManager &m) : m(m) {}
 private:
   FreeTypeManager &m;
 };
 int main() {
    FreeTypeManager m;
    ...
    SomeOtherClass c(m);
 }

解决办法是让它保持普通的c++ class,但只是在main() 的开头实例化它。这会将 initialisation/destruction 移动到一个稍微不同的地方。您需要将对 FreeTypeManager 的引用传递给每个想要通过构造函数参数使用它的 class。

请注意,使用 main() 而不是其他函数很重要;否则你会遇到范围界定问题,需要思考如何处理..