C#/ C ++中的非虚拟接口设计模式

在设计界面时,有人建议使用非虚拟界面模式。 有人可以简要概述这种模式的好处吗?

非虚拟接口模式的本质是您具有私有虚拟function,这些function由公共非虚拟function(非虚拟接口)调用。

这样做的好处是,基类对行为的控制比对派生类能够覆盖其接口的任何部分的控制要多。 换句话说,基类(接口)可以提供有关其提供的function的更多保证。

举个简单的例子,考虑一下这个有着几个典型派生类的好老动物类:

class Animal { public: virtual void speak() const = 0; }; class Dog : public Animal { public: void speak() const { std::cout << "Woof!" << std::endl; } }; class Cat : public Animal { public: void speak() const { std::cout << "Meow!" << std::endl; } }; 

这使用了我们习惯的常用公共虚拟接口,但它有一些问题:

  1. 每个派生的动物都在重复代码 - 唯一改变的部分是字符串,但每个派生类需要整个std::cout << ... << std::endl; 样板代码。
  2. 基类不能保证speak()作用。 派生类可能会忘记新行,或者将其写入cerr或其他任何内容。

要解决此问题,您可以使用非虚拟接口,该接口由允许多态行为的私有虚拟函数补充:

 class Animal { public: void speak() const { std::cout << getSound() << std::endl; } private: virtual std::string getSound() const = 0; }; class Dog : public Animal { private: std::string getSound() const { return "Woof!"; } }; class Cat : public Animal { private: std::string getSound() const { return "Meow!"; } }; 

现在,基类可以保证它将写出到std::cout并以新行结束。 它还使维护更容易,因为派生类不需要重复该代码。

Herb Sutter写了一篇关于非虚拟接口的好文章 ,我建议你查看。

这是一篇维基文章,它通过一些例子进行了详细介绍。 实质是您可以在基类的中心位置确保重要条件(如获取和释放锁),同时仍允许从中派生以通过使用私有或受保护的虚函数来提供不同的实现。

类层次结构的任何类的用户将始终调用公共接口,该公共接口将调用分派给不可在外部看到的实现。