模板和依赖注入

时间:2011-04-12 04:48:13

标签: c++ dependency-injection

我有一个类模板ResourceManager,它的用途是这样的:

ResourceManager<Image>* rm =
    ResourceManager<Image>::Instance();

Image* img = rm->acquire("picture.jpg");
rm->release(img);

我想使用依赖注入(将ResourceManager作为参数传递给应该使用它而不是全局使用的函数),但是鉴于它是一个模板,我不知道如何去做这个。你有什么建议吗?

我的游戏仅在开发的开始阶段,我已经有四种资源类型(ImageFontAnimationSound)所以制作一个{具有每种类型资源的获取功能的{1}}(即不是模板)不是一种选择。


编辑:一些澄清。

我正在寻找的不是如何为一个类型的ResourceManager进行依赖注入,而是一次性为所有这些进行依赖注入。

我的ResourceManager个对象在初始化/打开时需要加载资源;他们通过GameState这样做。但是,ResourceManagers可能需要加载任意数量的资源类型:动画,字体,图像,声音等 - 这是每种GameStates的许多函数参数!你有什么建议我做的?

4 个答案:

答案 0 :(得分:1)

好吧,如果函数需要特定类型的资源(可能是大多数资源),只需将特定模板实例定义为参数:

function(ResourceManager<Image> *rm, ...);

如果该功能需要任何类型的资源

  1. 成为模板本身,例如:

    template <typename T>
    function(ResourceManager<T> *rm, ...);
    

    它可能需要引用从资源管理器获取的资源,因此无论如何它都需要更多地方的模板参数。

  2. 使用多态基类。这意味着你必须定义像

    这样的东西
    class ResourceManagerBase { /* methods you need to call via the base class */ };
    template <typename T>
    class ResourceManager : ResourceManagerBase { ... };
    
    function(ResourceManagerBase *rm, ...)
    

    该函数可以调用基类中定义的任何方法。如果方法在内部依赖于资源类型,则它们将在基础中声明为抽象虚拟(virtual returnType method(...) = 0)并在模板类本身中定义。您还可以使用dynamic_cast来检查您所拥有的ResourceManager的具体实例。

    注意,如果函数需要引用资源,那么您同样需要所有资源的ResourceBase抽象基类,因此您可以引用任何类型的资源。

  3. 选择是在具有模板功能的较快但非常大的代码之间进行权衡(该功能将针对每个专业化单独编译)或者使用虚拟方法较慢但较小的代码(调用虚拟方法较慢,但有没有代码重复)。此外,模板变体编译速度较慢,因为大多数编译器将为使用它的每个目标文件生成代码,而不是在链接时合并相同的副本。

答案 1 :(得分:0)

使用构造函数注入的示例:

template<typename T>
struct ResourceManager
{
  virtual T* acquire(std::string const& resourceName)
  { return ...; }
  ... etc ...
};

class ImageUser
{
  ResourceManager<Image>* rm_;

  public:
  explicit ImageUser(ResourceManager<Image>* rm)
    : rm_(rm)
  {}

  ImageUser()
    : rm_(ResourceManager<Image>::Instance())
  {}

  void UseImage()
  {
    Image* img = rm_->acquire("picture.jpg");
    rm_->release(img);
  }
};


struct ImageResourceManagerFake : ResourceManager<Image>
{
  virtual Image* acquire(std::string const& resourceName) // override
  { return <whatever-you-want>; }
  ... etc ...
};

void test_imageuser()
{
  ImageResourceManagerFake* rm = new ImageResourceManagerFake;
  ImageUser iu(rm);
  ... test away ...
}

请注意,我遗漏了所有资源管理;在适用的地方使用智能指针等。

答案 2 :(得分:0)

首先。请记住,在使用模板时,必须在编译时解决所有问题。

然后代码中的ResourceManager似乎是单例。因此,粗略地讲,与全局变量没有区别。

当你可以直接调用单身人士时,我认为这是无用的传递rm作为你的函数的参数。

我希望,这可以解决你的问题。

答案 3 :(得分:0)

即使在你的编辑之后,我发现有点难以理解你所看到的是什么而没有看到整个画面,但我会再次尝试一个基本的例子:

template<typename T>
struct ResourceManager
{
  virtual T* acquire(std::string const& resourceName)
  { return ...; }
  // ... etc ...
};

class ImageUser
{
  ResourceManager<Image>* rm_;

  public:
  explicit ImageUser(ResourceManager<Image>* rm)
    : rm_(rm)
  {}

  void UseImage()
  {
    Image* img = rm_->acquire("picture.jpg");
    rm_->release(img);
  }
};

template<typename T>
struct ResourceManagerFake : ResourceManager<T>
{
  T* acquireRetVal;
  virtual T* acquire(std::string const& resourceName)
  { return acquireRetVal; }
  // ... etc ...
};

void test_imageuser()
{
  ResourceManagerFake<Image>* rm = new ResourceManagerFake<Image>;
  rm->acquireRetVal = new Image;
  ImageUser iu(rm);

  iu.UseImage();
}

评论者注意:我很清楚资源泄漏,但这不是重点。