Ich versuche zu verstehen, die echte Anforderung der Verwendung von Vorlagen für Policy-basierte Design. Beim Durcharbeiten der neuen Template-Designs in C++ fand ich heraus, dass das richtlinienbasierte Klassendesign eine sehr vorgeschlagene Art des Designs ist, die es ermöglicht, verschiedene Verhaltensweisen von Policy-Klassen "einzufügen". Ein minimales Beispiel ist die folgende (eine verkürzte Version des Wikis):Wann templated richtlinienbasiertes Design gegenüber nicht-templated Vererbung basierend Design
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!"
Eine schnelle Analyse zeigt, dass nur message()
verschiedene steckbare Methoden erhalten wir von einem Templat-Typ, deren Definition erben kann von jedermann zur Verfügung gestellt werden (und zur Kompilierzeit identifiziert).
Aber das gleiche Maß an Abstraktion (und konfigurierbare Methoden) kann erreicht werden, ohne einen Vorlagencode zu verwenden und durch den einfachen Old-School-Laufzeitpolymorphismus wie unten gezeigt.
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();
Funktionalität und Abstraktionsebene weise ich sehe nicht viel Unterschied in den beiden Ansatz (obwohl der zweite Ansatz paar zusätzliche Zeilen Code hat für LanguagePolicy
, ich denke, dass es für die anderen Benutzer benötigt wird, die Schnittstelle zu kennen, sonst ist das Verständnis LanguagePolicy
von der Dokumentation abhängig). Aber ich denke, dass das später "sauber" sein wird (kommt von jemandem, der die Vorlage nicht oft benutzt hat). Dies liegt daran, dass meiner Meinung nach nicht-templatierte Klassen sauberer anzusehen und zu verstehen sind. Ein sehr gutes Beispiel ist die beliebte Bibliothek VTK (Visualization Tool Kit), die mit dem zweiten Ansatz viele verschiedene Probleme löst. Obwohl es nicht umfangreiche Dokumentationen von VTK gibt, können die meisten von uns - ihre Benutzer - einfach in ihre Klassendiagramme schauen (manchmal sind sie ziemlich groß) und Verhaltensweisen von Klassen ableiten; und entwickeln hochkonfigurierbare und komplizierte Pipelines in unserer Anwendung (VTK kann nicht als Template-basiert abgebildet werden :)). Das Gegenteil sind Bibliotheken wie STL/BOOST, von denen ich glaube, dass niemand in der Lage ist, die Arbeit der Klassen ohne die Verwendung umfangreicher Dokumentation zu identifizieren.
Also meine Frage ist, ist das Template-basierte Policy-Design wirklich überlegen (nur in diesem Szenario der Policy-basierte Design) als virtuelle Vererbung basiert? Wenn ja, wann und warum?
-> Vorlage 'simuliert' Laufzeit Polymerphism durch Generieren von Klassen zur Kompilierzeit. Also, warum nicht direkt Runtime verwenden? Warum kümmern wir uns? -> Ich werde einen Blick werfen, aber wie hoch ist der Aufwand für virtuelle Anrufe wirklich? Sind die signifikant genug, um einen Effekt zu verursachen? – krips89
@ krips89 Der zweite Punkt. Bei virtuellen Aufrufen tritt ein Leistungsaufwand auf, der in einigen Fällen wichtig sein kann. –
@ krips89 Es kommt aber darauf an, wenn man die Indirection, das Durcheinander mit den Cache-Zeilen, die Möglichkeit einer Template-Funktion berücksichtigt, kann es einen Gewinn aus der Verwendung von Policies geben. –