为什么喜欢模板方法而非依赖注入?

问题描述 投票:9回答:3

我一直在阅读Gamma等人的Design Patterns。与依赖注入相比,我有一个关于模板方法的问题。

使用模板方法,您可以使用策略“模板化”类,这些策略提供所需操作或计算的替代方法。因此,不是从多个备选方案中选择一个策略并将该策略编码到类中,而是允许类的用户指定他们想要使用的替代方案。

这听起来对我来说非常合理。但我打了一个概念性的砖墙。

如果使用策略对象实例化类,则策略对象需要实现抽象接口。然后,程序员可以编写所有编译到类中的不同策略,而不会出现错误,因为策略实现了接口。使用策略的类将编码到策略接口而不是实现。

如果你要为这些策略对象定义一个抽象的IPolicy,为什么不使用依赖注入并在构造中传入IPolicy

任何人都可以解释为什么你更喜欢模板方法依赖注入?

c++ templates dependency-injection software-design
3个回答
14
投票

关于“模板方法”(而不是设计模式),以下示例可能有助于确定要做什么的优点和缺点。该示例是创建用于帮助调试/开发的库的详细模式。

随着模板

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 called\n");
}

然后库用户可以写:

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确实具有较小的成本。对于性能关键区域,这可能是也可能不是可接受的。


7
投票

首先,正如菲涅尔所提到的,模板方法模式不是现代意义上的模板。再次阅读它,它使用运行时多态来实现STL算法使用编译时多态性(函数模板)的相同目标。

其次,从某种意义上说,各种多态性都是依赖注入。也就是说,调用者将算法引入其作用的具体类型。所以问题一般不是你是否能够或应该使用依赖注入而不是其他模式:相反,模式显示了构造代码以使用依赖注入的有用方法。

如果您的“注入依赖项”是模板类型参数,则算法使用duck typing并且您不必实现抽象接口:只需编写具有预期签名的方法。


2
投票

在你的情况下“模板方法”(不要与Template Method Pattern混淆),或者让我们称之为“静态依赖注入”,将避免使用虚函数。您主要通过为编译器提供更多和明确的知识来获得性能,从而为他提供更好的优化机会。这些类变得更加静态,类型安全性也会增加。

类大小可能会缩小(不存储或减少存储指针的需要)。

俗话说:

不要为你不使用的东西买单。

这里适用。如果您不需要虚拟接口,模板可帮助您避免使用虚拟接口,而不会牺牲所有灵活性。

© www.soinside.com 2019 - 2024. All rights reserved.