对象管理——容器或工厂

Object Managment - Container or Factory?

本文关键字:工厂 管理 对象      更新时间:2023-10-16

我的游戏编程入门课程即将结束,我想将我在课堂上学到的技能与我以前的OOP经验结合起来,创建一个用于制作2D游戏的小型库。但是,我目前关心的是,它是管理类实例集合的最佳方法。

我使用的库(DarkGDK)完全由作用于整数的自由函数组成。当使用dbSprite()等函数创建"对象"时,您可以给它一个唯一的ID (int值)来引用它——我猜是某种"地址"。我个人觉得这种方法很可怕,所以我创建了类来封装每一组自由函数,如Image, Sprite和AnimatedSprite(这两种精灵类型在DarkGDK库中是不同的)。

问题是,为了使这些对象工作,我仍然必须向构造函数传递一个唯一的ID,以便针对适当的地址调用DarkGDK函数。我试着不再用ID来指代这些东西,但我在讨论应该如何创建这些对象。目前,我有一些AssetManager类保存对每个创建对象的引用,检查现有的ID,只允许唯一的ID,但这仍然不能解决被迫在管理类外部生成ID的问题。这使我认为工厂将是最好的方法。

我知道在c#中我可以创建一个AssetFactory<T> where T:Asset,它可以很容易地为每个资产调用适当的构造函数来创建实例,但据我所知c++没有这样的功能。

所以我认为我应该采取的方法是使用某种抽象的AssetFactory。我的想法(正确与否)是,AssetFactory的子对象将跟踪正在使用的id,并且只发出适当的对象唯一id。像这样:

class Asset {
    int m_index;
    Asset(int index);
};
class Image : public Asset {
    Image(char* imgPath);
    void Draw();
};
class Sprite : public Asset {
    Sprite(Image* img);
    void Draw();
};
class AssetFactory {
private:
    std::vector<Asset*> m_assets;
    int GetUniqueID();
public:
    AssetFactory();
    ~AssetFactory();
    virtual Asset* CreateAsset(); // but each class has different constructor parameters...
};
class ImageFactory : public AssetFactory {
    Asset* CreateAsset(char* imgPath); // ...so this wouldn't work (nor compile)
};
class SpriteFactory : public AssetFactory {
    Asset* CreateAsset();   // ...so will i be forced to call the default constructor and modify it later?
};
这里的问题是,如上所述,不同的对象有不同的构造函数,使得这种设计没有意义。我该换个方法吗?还是我对工厂模式有错误的理解?

编辑:澄清一下,我想要精灵和图像分开工厂的原因是因为它允许精灵和图像具有相同的ID。id必须仅在相同类型的其他资产中是唯一的。"

如果你的库允许任意id,并且你在一个相对相等的地址空间中工作(例如sizeof(int) == sizeof(int*)),这在我所知道的几乎所有32位编译器上都是正确的,这是一个非常微不足道的问题。生成id就很简单了——只需对指针重新进行interpret_cast即可。

class Sprite {
    int GetUniqueID() { return reinterpret_cast<int>(this); } // easies
public:
    // public interface
};

此外,重用旧的id实际上可能不值得。只要不断创造新的。我的意思是,你不会在32位整数中耗尽空间。

最后,这里绝对不能使用运行时继承。如果有必要,可以使用编译时的mixin。

c#有泛型,c++有模板。在c++中,你不能这么容易地提供对泛型参数的约束,但是有一些方法可以确保你只给出一个从资产派生的类型作为模板参数。

为了将适当的参数传递给构造函数,可以使用可变的模板方法。目前我没有编译器可用……虽然您可以在stackoverflow上找到许多其他可变模板代码,但稍后我将使用一个示例再次进行编辑。

也许我错过了一些东西,我不清楚你为什么不能在AssetManager中移动ID生成器,这样就隐藏了所有来自外部世界的唯一ID的痛苦。

无论如何,如果你需要跟踪id,你将需要manager类,据我所知,在这一点上,从你的帖子。如果你使用工厂方法而不是工厂类,你已经到达了终点线:)剩下的唯一问题是id的处理,但你可以在资产类的虚拟析构函数中做。如果你想保持它的干净,那么你应该在管理器中为它提供一个受保护的方法,并使资产类的析构函数(或一些清理函数)成为管理器的朋友。