Sind sie gleichwertig praktisch? Ich würde gerne praktische Unterschiede zwischen den Mustern wissen.
Sie sind konzeptionell unterschiedlich.
Für das erste Muster, Sie haben Dekorateure (transparent) über eine Kernfunktionalität Klasse gehen, die jeweils ihre eigenen Dreh/Spezialisierung auf eine vorhandene Implementierung hinzuzufügen.
Die Beziehung die ersten Mustermodelle sind "ist-a" (MyTypeWithMixins
ist eine Mixin1<MyType>
Spezialisierung, Mixin1<MyType>
ist eine MyType
Spezialisierung).
Dies ist ein guter Ansatz, wenn Sie die Funktionalität innerhalb einer starren Schnittstelle implementieren (da alle Typen die gleiche Schnittstelle implementieren).
Für das zweite Muster haben Sie Funktionalität Teile als Implementierungsdetails verwendet (möglicherweise in verschiedenen, nicht verwandten Klassen).
Die hier modellierten Beziehung „ist in Bezug auf die Umsetzung“ (MyTypeWithMixins
ist eine MyType
Spezialisierung in Bezug auf Mixin1
und Mixin2
Funktionalität implementiert). In vielen CRTP-Implementierungen wird die CRTP-basierte Basis als privat oder geschützt geerbt.
Dies ist ein guter Ansatz, wenn Sie gemeinsame Funktionalität implementieren accross verschiedene, unabhängige Komponenten (das heißt nicht mit der gleichen Schnittstelle).Dies liegt daran, dass zwei Klassen, die von Mixin1 erben, nicht dieselbe Basisklasse haben.
Um ein konkretes Beispiel für jeden bieten:
Für den ersten Fall betrachten wir die Modellierung einer GUI-Bibliothek. Jedes visuelle Steuerelement würde eine (beispielsweise) display
-Funktion haben, die in einem ScrollableMixin bei Bedarf Rollbalken hinzufügt; Der Scrollbalken mixin würde eine Basisklasse für die meisten Kontrollen sein, die wieder ansehnlich sind (aber alle von ihnen einen Teil der „Kontrolle/visuelle Komponente/darstellbaren“ Klassenhierarchie.
class control {
virtual void display(context& ctx) = 0;
virtual some_size_type display_size() = 0;
};
template<typename C>class scrollable<C>: public C { // knows/implements C's API
virtual void display(context& ctx) override {
if(C::display_size() > display_size())
display_with_scrollbars(ctx);
else
C::display(canvas);
}
...
};
using scrollable_messagebox = scrollable<messagebox>;
In diesem Fall wird alle mixin Typen würde eine Anzeigemethode überschreiben und Teile ihrer Funktionalität (das spezialisierte Zeichnungsteil) an den dekorierten Typ (die Basis) delegieren
Für den zweiten Fall einen Fall betrachten, in dem Sie ein internes System implementieren würden zum Hinzufügen einer Versionsnummer zu serialisierten Objekten innerhalb der Anwendung Die Implementierung würde wie folgt aussehen:
template<typename T>class versionable<T> { // doesn't know/need T's API
version_type version_;
protected:
version_type& get_version();
};
class database_query: protected versionable<database_query> {};
class user_information: protected versionable<user_information> {};
In diesem Fall speichern sowohl database_query
als auch user_information
ihre Einstellungen mit einer Versionsnummer, aber sie befinden sich in keiner Weise in derselben Objekthierarchie (sie haben keine gemeinsame Basis).
Dies ist nicht, was CRTP ist. Sicher, CRTP beinhaltet die allgemeine Struktur 'Klasse Child: Parent', aber der springende Punkt ist, dass die Basisklasse -'Parent ', über die Child-Klasse' Child' (wie? Es ist ein Template-Parameter!) Parent kann dann referenziere Dinge, die im Kind selbst definiert sind - zB kann es einen Operator! = aus dem Operator der Kindklasse == erzeugen. –
@HWalters, nicht unbedingt. In dem obigen Beispiel gibt die Implementierung von "versionierbar" keine Einschränkungen für "T" vor (das heißt, es muss nichts darüber wissen). Dies wurde absichtlich gemacht. –
utnapistim
Ich habe mich geirrt; Verwenden von "Child", um "Parent" zu instanziieren, einfach als praktischer Unifikator für den Basistyp. Aber ich denke, Sie haben den falschen Punkt angeführt - die Nichtauferlegung von "T" reicht nicht aus, um die Verwendung des CRTP zu rechtfertigen. Insbesondere kämpfe ich mit deinem Beispiel; Das Endergebnis ist, dass "database_query" -Objekte und "user_information" -Objekte version_ und get_version-Member haben, die sogar vom gleichen Typ sind. Welchen Vorteil hat die Verwendung des CRTP, um sie zu injizieren? (d. h. sicher, die Basisklassen sind unterschiedliche Typen, aber in welcher nützlichen Weise wäre das wichtig?) –