为什么更喜欢模板方法而不是依赖注入
Why prefer template method over dependency injection?
我一直在阅读Gamma等人的《设计模式》。我有一个关于模板方法和依赖注入的问题。
使用Template Method,您可以用策略"模板"类,这些策略为所需的操作或计算提供替代方案。因此,不是从几个备选策略中选择一个策略并将该策略编码到类中,而是允许类的用户指定他们想要使用的备选策略。
我听起来很有道理。但是我在概念上遇到了一点障碍。
如果你用策略对象实例化一个类,策略对象需要实现一个抽象接口。然后,程序员可以编写不同的策略,这些策略都不会出错地编译到类中,因为这些策略实现了接口。使用策略的类被编码到策略接口,而不是实现。
如果你要为这些策略对象定义一个抽象的IPolicy
,为什么不直接使用依赖注入并在构建时传递IPolicy
呢?
谁能解释一下为什么你更喜欢模板方法而不是依赖注入?
关于"模板方法"(而不是设计模式),下面的示例可能有助于决定该做什么。该示例创建一个库的详细模式,以帮助调试/开发。
与模板struct console_print
{
static void print(const string& msg) {std::cout<<msg;}
};
struct dont_print
{
static void print(const string& msg) {}
};
template<printer>
void some_function()
{
printer::print("some_function calledn");
}
库用户可以写:
some_function<console_print>(); //print the verbose message;
some_function<dont_print>(); //don't print any messages.
这段代码的好处是,如果用户不想打印代码,那么对dont_print::print(msg)
的调用完全从代码中消失(空静态类很容易被优化掉)。这样的调试消息甚至可以输入到性能关键区域。
模板的缺点是需要在编译之前决定策略。你还需要修改你的模板的函数/类签名。
没有模板当然可以这样做:
struct printer
{
virtual void print(const std::string& msg) = 0;
}
struct console_print : public printer
{
void print(const std::string& msg) {std::cout<<msg;}
}
struct debug_print : public printer
{
void print(const std::string& msg) {}
}
这样做的好处是,您可以将打印机类型传递给类和函数,并在运行时更改它们(对于某些应用程序可能非常有用)。然而,代价是总是对虚函数进行调用,因此空的dont_print确实有一个小代价。对于性能关键区域,这可能是可接受的,也可能是不可接受的。
首先,正如phresnel所提到的,模板方法模式不是现代意义上的模板。再读一遍,它使用运行时多态性来实现与STL算法使用编译时多态性(函数模板)相同的目标。
第二,从某种意义上说,所有类型的多态性都是依赖注入。也就是说,调用者将算法引入它所作用的具体类型。因此,问题通常不在于是否可以或应该使用依赖注入而不是使用其他模式:而是该模式显示了构建代码以使用依赖注入的有用方法。
如果你的"注入的依赖项"是一个模板类型参数,算法使用duck类型,你不需要实现一个抽象接口:只需编写具有预期签名的方法。
在你的例子中,"模板方法"(不要与模板方法模式混淆),或者让我们称之为"静态依赖注入",将避免对虚函数的需要。您主要通过向编译器提供更多明确的知识来获得性能,从而为他提供更好的优化机会。类变得更加静态,类型安全性得到提高。
类大小可能会缩小(不需要或减少需要存储指针)。
俗话说:
不要为你不用的东西付钱。
在这里也同样适用。如果你不需要虚拟接口,模板可以帮助你在不牺牲所有灵活性的情况下避免它们。
- 如何在不诉诸依赖注入的情况下模拟遗留函数?
- 正确构造和销毁 Setter 依赖注入对象(可能使用 qt)
- 依赖注入:依赖关系需求字段/成员
- 模板或链接接缝依赖注入有哪些替代方案来测试非虚拟方法?
- 类型擦除代码的依赖注入单元测试
- 包装类设计和依赖注入
- 在进行依赖注入时公开私有方法
- 有趣的C++依赖注入方案
- 使用X3对解析器的依赖注入
- 我如何使用Gmock进行依赖注入
- 重构Singleton/Globals以使用依赖注入进行单元测试
- 依赖注入/继承设计模式的构造函数参数太多
- 依赖注入 + 完全虚拟与接口
- 依赖注入,unique_ptr模拟
- 构造函数依赖注入:unique_ptr + move vs shared_ptr
- 为什么更喜欢模板方法而不是依赖注入
- c++依赖注入:对象生命周期
- 我可以在依赖注入中使用std::unique_ptr吗?
- 工厂类的依赖注入
- c++依赖注入+得墨忒耳定律+ logger/assert