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

本文关键字:虚拟 接口 设计模式 C++ | 更新日期: 2023-09-27 18:00:18

在设计接口时,有人建议使用非虚拟接口模式。有人能简要介绍一下这种模式的好处吗?

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

非虚拟接口模式的本质是拥有私有虚拟函数,这些函数由公共非虚拟

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

作为一个简单的例子,考虑好的老动物类和几个典型的派生类:

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写了一篇关于非虚拟接口的好文章,我建议你去看看。

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

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