2011-06-25 7 views

Odpowiedz

24

Istotą wzoru zakaz wirtualnego interfejsu jest to, że masz prywatnych wirtualnych funkcje, które są wywoływane przez publicznych non-wirtualnych funkcji (interfejs nie wirtualnych).

Zaletą tego jest to, że klasa bazowa ma większą kontrolę nad swoim zachowaniem, niż gdyby klasy pochodne były w stanie nadpisać dowolną część jego interfejsu. Innymi słowy, klasa bazowa (interfejs) może zapewnić więcej gwarancji co do funkcjonalności, którą zapewnia.

Jako prosty przykład rozważmy starą dobrą klasę zwierząt z kilku typowych klas pochodnych:

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

używa zwykle wirtualny interfejs publiczny że jesteśmy przyzwyczajeni, ale ma kilka problemy:

  1. Każde zwierzę pochodzące powtarza kod - tylko część, która zmienia to ciąg, ale każda klasa pochodna musi cały kod std::cout << ... << std::endl; szablonowe.
  2. Klasa podstawowa nie może zagwarantować, co robi speak(). Klasa pochodna może zapomnieć o nowej linii lub napisać ją do cerr lub cokolwiek innego.

Aby rozwiązać ten problem, można użyć innych niż wirtualny interfejs, który jest uzupełniony przez prywatnego wirtualnego funkcja pozwalająca polimorficzne:

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

Teraz klasa bazowa może zagwarantować, że będzie pisać do std::cout i kończy się nową linią. Ułatwia także konserwację, ponieważ klasy pochodne nie muszą powtarzać tego kodu.

Herb Sutter napisał a good article on non-virtual interfaces, który polecam sprawdzić.

+1

Czy zapomniałeś o wirtualnych destruktorach lub pominąłeś je dla zwięzłości, czy też jest w tym przypadku w porządku (brak danych w dowolnym miejscu w hierarchii)? –

+0

to również działa dobrze z prywatnego dziedziczenia. –

3

Tutaj jest wiki article to trochę więcej szczegółów z kilkoma przykładami. Istotne jest to, że można zapewnić ważne warunki (takie jak uzyskiwanie i zwalnianie blokad) w centralnym miejscu w klasie bazowej, a jednocześnie umożliwiać uzyskiwanie z niego różnych implementacji za pomocą prywatnych lub chronionych funkcji wirtualnych.

Użytkownicy dowolnej klasy w hierarchii klas będą zawsze wywoływać interfejs publiczny, który wywoła wywołania do niewidocznie widocznych implementacji.