C++ 中构造函数和模板参数之间的依赖注入权衡?
Dependency injection tradeoff between constructor and template parameters in C++?
我想知道何时通过构造函数使用依赖注入以及何时使用模板参数.
例子
我有以下 class 定义:
Interface
:
/// Interface class
class Interface {
public:
virtual void Method() = 0;
virtual ~Interface() = default;
};
Concrete
:
/// Concrete class
class Concrete : public Interface {
public:
void Method() override { std::cout << "Concrete.Method()\n"; }
static void StaticMethod() { std::cout << "Concrete.StaticMethod()\n"; }
};
ConstructorDI
通过构造函数依赖于 Interface
:
/// Class with constructor dependency injection
class ConstructorDI {
public:
explicit ConstructorDI(Interface &interface) : interface_(interface) {
std::cout << "** ConstructorDI() **" << std::endl;
// Call the Method() exposed by the interface
interface_.Method();
}
private:
// Interface-based dependency (this class doesn't own the underlying concrete
// type)
Interface &interface_;
};
TemplateDI
依赖于 typename T
/// Class with template dependency injection
template <typename T>
class TemplateDI {
public:
TemplateDI() {
std::cout << "** TemplateDI() **" << std::endl;
// Call the Method()
t_.Method();
// Call the StaticMethod()
T::StaticMethod();
}
private:
// Type-based dependency (this class owns the concrete type)
// Thus, cannot be a Interface (pure abstract) class
T t_;
};
上面的典型用法如下example:
// Concrete class implementing the Interface class
Concrete concrete;
// Create a class using dependency injection via constructor
ConstructorDI constructor_d_i(concrete);
// Create a class using dependency injection via template
TemplateDI<Concrete> template_d_i;
产生以下输出:
** ConstructorDI() **
Concrete.Method()
** TemplateDI() **
Concrete.Method()
Concrete.StaticMethod()
观察
- 上面的例子表明 构造函数依赖 路由允许依赖 class 本身不拥有的具体类型。
- 基于模板的依赖注入允许class拥有具体类型,但也可用于依赖静态方法。
问题
要在这两种类型之间进行权衡,我想知道:
- 我是否缺少另一种 C++ 依赖注入方法?
- 在可测试性方面是否存在重大差异?
- 我什么时候应该使用一个或另一个,例如如观察所述?
使用模板与基于继承的多态总是有同样的权衡:模板实例化发生在编译时,因此允许编译时多态行为。
基于继承的多态性发生在运行时。因此,例如,您可以存储指向您在编译时不知道其类型的对象的指针集合,例如
//...
std::vector<Interface*> some_stuff = get_heterogeneous_concrete_types();
std::vector<ConstructorDI> di_objects;
std::transform(some_stuff.begin(), some_stuff.end(), std::back_inserter(di_objects),
[](auto* p) { return ConstructorDI(*p); }
);
//...
如果您有多个在具体类型上参数化的 DI 类型,则不能执行上述操作。但是如果你不需要那种行为,模板化版本会更有效率,等等。
我想知道何时通过构造函数使用依赖注入以及何时使用模板参数.
例子
我有以下 class 定义:
Interface
:
/// Interface class
class Interface {
public:
virtual void Method() = 0;
virtual ~Interface() = default;
};
Concrete
:
/// Concrete class
class Concrete : public Interface {
public:
void Method() override { std::cout << "Concrete.Method()\n"; }
static void StaticMethod() { std::cout << "Concrete.StaticMethod()\n"; }
};
ConstructorDI
通过构造函数依赖于Interface
:
/// Class with constructor dependency injection
class ConstructorDI {
public:
explicit ConstructorDI(Interface &interface) : interface_(interface) {
std::cout << "** ConstructorDI() **" << std::endl;
// Call the Method() exposed by the interface
interface_.Method();
}
private:
// Interface-based dependency (this class doesn't own the underlying concrete
// type)
Interface &interface_;
};
TemplateDI
依赖于typename T
/// Class with template dependency injection
template <typename T>
class TemplateDI {
public:
TemplateDI() {
std::cout << "** TemplateDI() **" << std::endl;
// Call the Method()
t_.Method();
// Call the StaticMethod()
T::StaticMethod();
}
private:
// Type-based dependency (this class owns the concrete type)
// Thus, cannot be a Interface (pure abstract) class
T t_;
};
上面的典型用法如下example:
// Concrete class implementing the Interface class
Concrete concrete;
// Create a class using dependency injection via constructor
ConstructorDI constructor_d_i(concrete);
// Create a class using dependency injection via template
TemplateDI<Concrete> template_d_i;
产生以下输出:
** ConstructorDI() **
Concrete.Method()
** TemplateDI() **
Concrete.Method()
Concrete.StaticMethod()
观察
- 上面的例子表明 构造函数依赖 路由允许依赖 class 本身不拥有的具体类型。
- 基于模板的依赖注入允许class拥有具体类型,但也可用于依赖静态方法。
问题
要在这两种类型之间进行权衡,我想知道:
- 我是否缺少另一种 C++ 依赖注入方法?
- 在可测试性方面是否存在重大差异?
- 我什么时候应该使用一个或另一个,例如如观察所述?
使用模板与基于继承的多态总是有同样的权衡:模板实例化发生在编译时,因此允许编译时多态行为。
基于继承的多态性发生在运行时。因此,例如,您可以存储指向您在编译时不知道其类型的对象的指针集合,例如
//...
std::vector<Interface*> some_stuff = get_heterogeneous_concrete_types();
std::vector<ConstructorDI> di_objects;
std::transform(some_stuff.begin(), some_stuff.end(), std::back_inserter(di_objects),
[](auto* p) { return ConstructorDI(*p); }
);
//...
如果您有多个在具体类型上参数化的 DI 类型,则不能执行上述操作。但是如果你不需要那种行为,模板化版本会更有效率,等等。