Podczas projektowania interfejsu zaleca się użycie nie-wirtualnego wzorca interfejsu. Czy ktoś może krótko opisać, jakie są zalety tego wzoru?Nieprawidłowy wzór interfejsu interfejsu w języku C#/C++
Odpowiedz
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:
- 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. - Klasa podstawowa nie może zagwarantować, co robi
speak()
. Klasa pochodna może zapomnieć o nowej linii lub napisać ją docerr
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ć.
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)? –
to również działa dobrze z prywatnego dziedziczenia. –
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.
_Jaki jest wzór interfejsu innego niż wirtualny? –