【问题标题】:C++: Difference between NVI and Template Method Patterns?C++:NVI 和模板方法模式之间的区别?
【发布时间】:2010-06-20 09:20:51
【问题描述】:

NVI (Non-Virtual Interface) 和Template Method 模式有什么区别?

它们看起来非常相似,而且我读过它们基本相同,并且它们之间存在细微的差异,因为 Template 更通用。

【问题讨论】:

  • 我不会使用维基百科链接作为参考,他们对锁的使用在遇到异常时很脆弱......

标签: c++ design-patterns template-method-pattern non-virtual-interface


【解决方案1】:

NVI 是一个成语,Template Method 是一个模式。 NVI 是在 C++ 中使用动态调度的模板方法模式的实现;也可以使用模板元编程在 C++ 中创建模板方法来消除动态分派。

模式比惯用语更通用,语言可能使用不同的惯用语来实现模式。

【讨论】:

  • 所以您是说 NVI 基本上是模板方法模式的特定语言实现,除此之外没有真正的区别吗?您将如何使用 C++ 模板来实现相同的结果?
  • @Robert S. Barnes 据我所知,没有明显的方法可以使用 C++ 模板作为模板方法。模板方法说“先做这件事,然后再做另一件事”,虽然您可以为要做的一件或另一件事创建一个仿函数,但与 C++ 模板提供给您的类型参数没有真正的关系。
  • 我想我还是不明白你在这里的意思:“也可以使用模板元编程在 C++ 中创建模板方法来消除动态调度。”
  • 通常在模板方法中,有一些非虚拟调用和一些虚拟调用。您可以将模板方法放在模板中,或针对模板特化进行调用,以便在编译时解析调用,但您不能直接创建模板特化,即模板方法 - 您可以从模板调用模板特化方法,但模板特化不是模板方法本身。例如template <typename T> T fma ( T a, T b, T c ) { return a + b * c; } 将调用正确的 T::operator+ 特化或重载。
【解决方案2】:

如前所述,NVI 是一种编程习语,与一类语言有关。它由 Herb Sutter 等人推广,因为它有助于执行合同:

  • 类不变量
  • 函数契约(对传递的参数和生成的返回值的断言)
  • 重复操作(如日志记录)
  • 控制生成的异常(虽然是个坏主意;))

但是,实现实际上可能有很大不同,例如 NVI 实现的另一个示例是将其与 Pimpl 结合:

class FooImpl;

class Foo
{
public:
  enum type { Type1, Type2 };

  Foo(type t, int i, int j);

  int GetResult() const;

private:
  FooImpl* mImpl;
};

对于实施:

struct FooImpl
{
  virtual ~FooImpl();
  virtual int GetResult() const;
};

class FooType1: public FooImpl
{
public:
  FooType1(int i, int j);
  virtual int GetResult() const;
private:
  /// ...
};

我一直发现它更好地传达了这一点。你想清楚了吗?

主要的一点是virtual 是一个实现细节。在接口中公开实现细节是一个坏主意,因为您可能希望更改它们。

此外,实现细节往往会影响二进制兼容性。例如,在一个类中添加一个新的virtual 方法可能会改变虚拟表的布局(常见的实现技术),从而破坏二进制兼容性。在 gcc 上,如果您希望保持兼容性,您需要确保最后添加它(在虚拟中)。

通过使用上面的 NVI + Pimpl 组合,暴露的类中根本没有virtual(甚至不是私有的)。内存布局向后和向前兼容。我们已经实现了二进制兼容性。

在这里,我们一次使用多个模式:

  • 模板方法
  • 策略(因为我们可以随意交换指针)
  • 工厂(决定我们获得哪种实现)

【讨论】:

  • +1 表示“虚拟即实现细节”和 ABI 考虑因素。
  • +1。但是,我不同意您为虚拟接口创建单独的类似 pimpl 的类。最明显的缺点是它使所需的课程数量增加了一倍。此外,如果 FooImpl 不是不透明的,它可能会诱使人们直接使用它并跳过 Foo。但是,它不太可能是不透明的,因为 NVI 的目的是允许人们覆盖虚拟实现,因此 FooImpl 必须是可公开访问的。尽管它有自己的缺点,但我认为在一个类中实现 NVI 并同时避免公共虚函数是一种更好的方法。
  • 这也是一个更容易执行的策略:没有公共虚拟函数,而不是为提供公共虚拟接口的类似 pimpl 的类提供特殊情况。
  • virtual功能的问题不在于易用性,而在于ABI。 FooImpl 不透明的优点是您将其限制在您的二进制文件中,因此当您向客户提供新版本的二进制文件时不会出现 ABI 问题。
  • 很好,但是 NVI 与模板方法有何不同?看来您是在说 NVI 是在某一类语言中实现模板方法的特定方式?
猜你喜欢
  • 1970-01-01
  • 2012-04-16
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多