Шаблон не виртуального интерфейса в C # / C ++ - PullRequest
16 голосов
/ 26 июня 2011

При разработке интерфейса кто-то рекомендовал использовать шаблон невиртуального интерфейса. Может кто-нибудь вкратце рассказать о преимуществах этого паттерна?

Ответы [ 2 ]

31 голосов
/ 26 июня 2011

Суть невиртуального шаблона интерфейса состоит в том, что у вас есть частные виртуальные функции, которые вызываются общедоступными не виртуальными функциями (не виртуальный интерфейс).

Преимущество этого состоит в том, что базовый класс имеет больший контроль над своим поведением, чем если бы производные классы могли переопределять любую часть своего интерфейса.Другими словами, базовый класс (интерфейс) может предоставить больше гарантий относительно функциональности, которую он обеспечивает.

В качестве простого примера рассмотрим старый добрый класс животных с парой типичных производных классов:

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 и завершится новой строкой.Это также облегчает обслуживание, поскольку производным классам не нужно повторять этот код.

Херб Саттер написал хорошую статью о не виртуальных интерфейсах , которую я бы рекомендовал проверить.

3 голосов
/ 26 июня 2011

Вот статья вики , немного подробнее с некоторыми примерами. Суть в том, что вы можете обеспечить важные условия (например, получение и освобождение блокировок) в центральном месте вашего базового класса, в то же время позволяя использовать его для предоставления различных реализаций с использованием частных или защищенных виртуальных функций.

Пользователи любого класса иерархии классов всегда будут вызывать открытый интерфейс, который отправляет вызовы не видимым извне реализациям.

...