5

我有一个非常简单的游戏引擎。它使用了几个单例(我将列举其中的一些)。

Resource Manager
Render Engine
Events Manager
Factory
etc

这些单例有很多从一个到另一个的调用。我将采用事件管理器示例用法:

  1. 任何从 Listener 派生的对象都可以像这样添加 itsel 作为某些事件的侦听器EventsManager->RegisterListener(this, &SomeClass::SomeMethod);(事件类型由 SomeMethod 参数推断)
  2. 任何其他对象都可以触发这样的事件EventsManager->PushEvent(SomeEvent);

经过一些同步后,事件到达所有侦听器。这是单例时 EventsManager 的一个非常简单的用法。

其他单身人士也有类似的行为。我想删除单例,但我的主要问题是我想从“用户的角度”保持代码简单易用,就像现在一样。我阅读了一些这样做的技术,但大多数使类的初始化/使用更加复杂。我知道这个话题在 SO 上被讨论过很多次,但没有一个答案适合我的编程理念——让一切尽可能简单。

我不想为我的类进行复杂的定义/初始化,例如:

SomeClass<EventManager, RenderEngine,...>

或者

SomeClass::SomeClass(EventsManager, RenderEngine...)

你能给我一些关于这个话题的建议吗?

4

3 回答 3

2

您可以拥有一个全局“游戏”对象,该对象创建当前为单例的每个类的实例

对于您的 EventManager 的具体示例;您的 Listener 基类可以提供派生类可以调用的注册方法和推送方法的实现。

骨架定义:

class Listener
{
public:
    virtual void ReceiveMessage( ... ) = 0;

protected:
    void Register()
    {
        GetEventManagerSomehow()->RegisterListener( this, etc );
    }

    void PushEvent( etc )
    {
        GetEventManagerSomehow()->PushEvent( etc );
    }

}
于 2012-11-29T15:37:21.977 回答
1

要解决在单例中检测资源泄漏的具体问题,请为每个单例类提供一个销毁实例的关闭方法。

class Singleton
{
    // ...
    static Singleton * GetInstance()
    {
        if (instance == NULL)
            instance = new Singleton;
        return instance;
    }
    static void Shutdown()
    {
        delete instance;
        instance = NULL;
    }
    static Singleton * instance;
};

Singleton * Singleton::instance = NULL;
于 2012-11-29T15:42:49.113 回答
0

不是真正的答案,但评论可能太长了。

依赖注入是单例的一个很好的替代方案:您在程序的主函数中创建实例并将它们传递给“模块”(它们是主类),因此它们可以在本地使用它们。这意味着对于这些类,您将拥有您不想要的“复杂”构造函数。
但是,复杂性应该仅限于某些类,并且在我看来,传递一些依赖的“模块”并不那么复杂。作为奖励,您可以通过查看构造函数或主函数来找出模块之间的依赖关系。

依赖注入被大量使用,因为它确实解决了您所看到的问题(以及更多,如单元测试),而增加的复杂性非常有限。

于 2012-11-29T15:53:51.590 回答