2014-10-21 32 views
11

Ich studiere über Mixins (in C++). Ich las einige Artikel über Mixins und fand zwei verschiedene Muster von "approximierenden" Mixins in C++.Zwei verschiedene Mixin-Muster in C++. (Mixin? CRTP?)

Muster 1:

template<class Base> 
struct Mixin1 : public Base { 
}; 

template<class Base> 
struct Mixin2 : public Base { 
}; 

struct MyType { 
}; 

typedef Mixin2<Mixin1<MyType>> MyTypeWithMixins; 

Muster 2: (kann CRTP genannt werden)

template<class T> 
struct Mixin1 { 
}; 

template<class T> 
struct Mixin2 { 
}; 

struct MyType { 
}; 

struct MyTypeWithMixins : 
    public MyType, 
    public Mixin1<MyTypeWithMixins>, 
    public Mixin2<MyTypeWithMixins> { 
}; 

Sind sie gleichwertig praktisch? Ich würde gerne praktische Unterschiede zwischen den Mustern wissen.

Antwort

7

Der Unterschied ist die Sichtbarkeit. In dem ersten Muster ‚sind Mitglieder s direkt sichtbar und nutzbar durch die Mixins, ohne die Notwendigkeit für das Gießen und Mixin1MyType s Mitglieder sichtbar sind Mixin2. Wenn MyType auf Mitglieder von den Mixins zugreifen möchte, muss this gecastet werden, und es gibt keine gute Möglichkeit, dies sicher zu tun. Im zweiten Muster gibt es keine automatische Sichtbarkeit zwischen dem Typ und den Mixins, aber die Mixins können einfach und sicher this zu MyTypeWithMixins gießen und dadurch auf die Elemente des Typs und anderer Mixins zugreifen. (MyType könnte auch, wenn Sie die CRTP auch darauf angewendet.)

So kommt es auf Komfort versus Flexibilität. Wenn Ihre Mixins ausschließlich auf Dienste vom Typ zugreifen und keine eigenen Geschwisterabhängigkeiten haben, ist das erste Muster nett und direkt. Wenn ein Mix-In von Diensten des Typs oder anderer Mixins abhängt, sind Sie mehr oder weniger gezwungen, das zweite Pattern zu verwenden.

7

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).

+0

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. –

+0

@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

+0

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?) –

Verwandte Themen