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

时间:2011-11-15 09:56:47

标签: c++ templates dependency-injection software-design

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

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

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

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

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

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

3 个答案:

答案 0 :(得分: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确实具有较小的成本。对性能关键区域而言,这可能是可接受的,也可能是不可接受的。

答案 1 :(得分:7)

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

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

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

答案 2 :(得分:2)

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

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

俗话说:

  

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

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