模板和依赖注入
Templates and dependency injection
我有一个类模板ResourceManager
,它的目的是像这样使用:
ResourceManager<Image>* rm =
ResourceManager<Image>::Instance();
Image* img = rm->acquire("picture.jpg");
rm->release(img);
我想使用依赖注入(将ResourceManager
作为参数传递给应该使用它而不是全局使用它的函数),但是考虑到它是一个模板,我不知道如何做到这一点。你有什么建议吗?
我的游戏才刚刚开始开发,我已经有了四种资源类型(Image
, Font
, Animation
和Sound
),所以为每种类型的资源制作一个具有获取功能的单一ResourceManager
(即不是模板)不是一个选择。
编辑:一些澄清。
我要找的不是如何为一种类型的ResourceManager做依赖注入,而是一次为所有这些。
我的GameState
对象需要在初始化/打开时加载资源;他们通过ResourceManagers
这样做。然而,GameStates
可能需要加载任何类型的资源:动画、字体、图像、声音等。每种ResourceManager
都有很多函数参数!你建议我怎么做?
如果函数需要特定类型的资源(可能大多数都需要),只需将特定的模板实例定义为参数:
function(ResourceManager<Image> *rm, ...);
如果函数需要任何类型的资源,它可以
模板本身,如:
template <typename T> function(ResourceManager<T> *rm, ...);
它可能需要引用从资源管理器获得的资源,因此无论如何它将在更多的地方需要template参数。
使用多态基类。这就意味着你必须定义像
这样的东西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
抽象基类,以便您可以引用任何类型的资源。
选择是在使用模板函数的更快但非常大的代码(函数将为每个专门化单独编译)或使用虚拟方法的更慢但更小的代码(调用虚拟方法更慢,但没有代码重复)之间进行权衡。此外,模板变体将会编译得更慢,因为大多数编译器会为使用它的每个对象文件生成代码,然后在链接时合并相同的副本。
第一。记住,在使用模板时,必须在编译时解决所有问题。
那么你的代码中的ResourceManager似乎是一个单例。粗略地说,对于全局变量,没有什么区别。
我认为当你可以直接调用单例时,传递rm作为函数的参数是没有用的。
我希望这就解决了你的问题。
使用构造函数注入的例子:
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 ...
}
注意,我已经忽略了所有的资源管理;
即使在您的编辑之后,如果没有看到整个图片,我也很难理解您想要的是什么,但我将在一个基本示例中进行第二次尝试:
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();
}
注释:我很清楚资源泄漏,但这不是这里的重点。
- 限定的依赖名称查找,其中包含基类的注入类名
- 如何在不诉诸依赖注入的情况下模拟遗留函数?
- 类成员和依赖项注入的对象与引用
- C++如何在派生类上进行依赖项注入
- 正确构造和销毁 Setter 依赖注入对象(可能使用 qt)
- 依赖注入:依赖关系需求字段/成员
- 现代编译器是否可以在使用依赖关系注入时取消虚拟化函数调用
- 模板或链接接缝依赖注入有哪些替代方案来测试非虚拟方法?
- 类型擦除代码的依赖注入单元测试
- 包装类设计和依赖注入
- 在进行依赖注入时公开私有方法
- 是否可以在Qt设计器中使用依赖项注入(对于.ui文件)
- 有趣的C++依赖注入方案
- 使用X3对解析器的依赖注入
- 我如何使用Gmock进行依赖注入
- 重构Singleton/Globals以使用依赖注入进行单元测试
- 依赖关系注入和事件处理
- 依赖注入/继承设计模式的构造函数参数太多
- 依赖项注入在C++中有用吗
- 用水果注入C++依赖