Próbuję zrozumieć rzeczywisty wymóg stosowania szablonów do projektowania opartego na zasadach. Przechodząc przez nowe szablonowe szablony w C++, stwierdziłem, że projektowanie klas oparte na regułach jest wysoce sugerowanym sposobem projektowania, który umożliwia "podłączanie" różnych zachowań z klas polityk. Minimalny przykładem jest następujący (skrócona wersja wiki):Kiedy należy preferować projektowanie oparte na szablonach za pomocą projektowania opartego na dziedziczeniu niematerialnym
template <typename LanguagePolicy>
class HelloWorld : private LanguagePolicy
{
using LanguagePolicy::message;
public:
// Behaviour method
void run() const
{
// policy methods
cout << message();
}
};
class LanguagePolicyA
{
protected:
std::string message() const
{
return "Hello, World!";
}
};
//usage
HelloWorld<LanguagePolicyA> hello_worlda;
hello_worlda.run(); // prints "Hello, World!"
Szybka analiza pokazuje, że wystarczy, aby uzyskać różne metody wetknięcia message()
jesteśmy dziedziczy od typu matrycy, której definicja może być zapewnione przez nikogo (i identyfikowane podczas kompilacji).
Ale ten sam poziom abstrakcji (i metod konfigurowalnych) można osiągnąć bez użycia kodu szablonu i prostego polimorfizmu czasu pracy, jak pokazano poniżej.
class HelloWorld
{
LanguagePolicy *lp; //list of all plugable class
public:
HelloWorld(LanguagePolicy *lpn) {
lp = lpn;
}
// Behaviour method
void run() const
{
// policy methods
cout << lp->message();
}
};
class LanguagePolicy
{
protected:
virtual std::string message() const;
};
class LanguagePolicyA: LanguagePolicy
{
protected:
std::string message() const
{
return "Hello, World!";
}
};
//usage
HelloWorld helloworld(new LanguagePolicyA);
helloworld.run();
Funkcjonalność i poziom abstrakcji mądry nie widzę wielkiej różnicy w dwa podejścia (choć drugie podejście ma kilka dodatkowych linii kodu dla LanguagePolicy
, myślę, że jest to konieczne dla innych użytkowników znać interfejs, w przeciwnym razie zrozumienie LanguagePolicy
zależy od dokumentacji). Ale myślę, że później będzie "czysty" (pochodzący od kogoś, kto nie używał zbyt wiele szablonów). Dzieje się tak, ponieważ osobiście moim zdaniem zajęcia niezwiązane z szablonem są czystsze, aby je przejrzeć i zrozumieć. Bardzo dobrym przykładem jest popularna biblioteka VTK (Visualization Tool Kit), która rozwiązuje wiele różnych problemów przy użyciu drugiego podejścia. Mimo że nie ma obszernych dokumentacji VTK, większość z nas - jej użytkownicy, może po prostu przejrzeć swoje diagramy klas (czasami są one dość duże) i wydedukować zachowania klas; i opracować wysoce konfigurowalne i skomplikowane potoki w naszej aplikacji (nie można obrazować VTK jako opartego na szablonach :)). Przeciwieństwem są biblioteki takie jak STL/BOOST, które moim zdaniem nie są w stanie zidentyfikować pracy klas bez użycia obszernej dokumentacji.
Moje pytanie brzmi: czy projekt oparty na szablonach jest naprawdę lepszy (tylko w tym scenariuszu opartym na zasadach) niż oparty na dziedziczeniu wirtualnym? Jeśli tak, kiedy i dlaczego?
-> Szablon "symuluje" polimorfizm czasu pracy, generując klasy w czasie kompilacji. Dlaczego więc nie używać bezpośrednio środowiska uruchomieniowego? Dlaczego nas to obchodzi? -> Przyjrzę się, ale ile naprawdę kosztuje obciążenie wirtualne? Czy są wystarczająco znaczące, aby wywołać efekt? – krips89
@ krips89 Drugi punkt. Występuje narzut wydajności związany z połączeniami wirtualnymi, co może być ważne w niektórych przypadkach. –
@ krips89 To zależy, ale jeśli weźmiesz pod uwagę obojętność, bałagan z liniami pamięci podręcznej, możliwość wstawienia funkcji szablonowej, może zaistnieć korzyść z używania zasad. –