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

use*_*414 16 c# c++ design-patterns non-virtual-interface

在设计界面时,有人建议使用非虚拟界面模式.有人可以简要介绍一下这种模式的好处吗?

Pet*_*der 33

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

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

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

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; }
};
Run Code Online (Sandbox Code Playgroud)

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

  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!"; }
};
Run Code Online (Sandbox Code Playgroud)

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

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