user705414 user705414 - 3 months ago 15
C# Question

Non-virtual interface design pattern in C#/C++

When designing an interface, someone recommended to use the non-virtual interface pattern. Can someone briefly outline what the benefits of this pattern are?

Answer

The essence of the non-virtual interface pattern is that you have private virtual functions, which are called by public non-virtual functions (the non-virtual interface).

The advantage of this is that the base class has more control over its behaviour than it would if derived classes were able to override any part of its interface. In other words, the base class (the interface) can provide more guarantees about the functionality it provides.

As a simple example, consider the good old animal class with a couple of typical derived classes:

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; }
};

This uses the usual public virtual interface that we're used to, but it has a couple of problems:

  1. Each derived animal is repeating code -- the only part that changes is the string, yet each derived class needs the whole std::cout << ... << std::endl; boilerplate code.
  2. The base class can't make guarantees about what speak() does. A derived class may forget the new line, or write it to cerr or anything for that matter.

To fix this, you can use a non-virtual interface that is supplemented by a private virtual function that allows polymorphic behaviour:

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!"; }
};

Now the base class can guarantee that it will write out to std::cout and end with a new line. It also makes maintenance easier as derived classes don't need to repeat that code.

Herb Sutter wrote a good article on non-virtual interfaces that I would recommend checking out.

Comments