Czy są one równoważne praktycznie? Chciałbym poznać praktyczną różnicę między wzorami.
Są różne koncepcyjnie.
Dla pierwszego wzoru masz dekoratory (przezroczyste) nad podstawową klasą funkcjonalności, z których każda dodaje własną wersję/specjalizację do istniejącej implementacji.
Związek Pierwsze modele wzór „oznacza A” (MyTypeWithMixins
jest Mixin1<MyType>
specjalizacji Mixin1<MyType>
jest MyType
kierunek).
Jest to dobre podejście w przypadku implementowania funkcjonalności w sztywnym interfejsie (ponieważ wszystkie typy będą implementować ten sam interfejs).
W przypadku drugiego wzoru części funkcjonalne są używane jako szczegóły implementacji (prawdopodobnie w obrębie różnych, niepowiązanych klas).
Relacja wzorowana jest tu „realizowany jest w kategoriach” (MyTypeWithMixins
jest MyType
specjalizacja, realizowane pod względemMixin1
i Mixin2
funkcjonalności). W wielu implementacjach CRTP baza szablonowa CRTP jest dziedziczona jako prywatna lub chroniona.
Jest to dobre podejście w przypadku wdrażania wspólnej funkcjonalności za pośrednictwem różnych, niepowiązanych ze sobą komponentów (tj. Bez tego samego interfejsu).Dzieje się tak, ponieważ dwie klasy dziedziczące z Mixin1 będą , a nie mają tę samą klasę podstawową.
Aby zapewnić konkretny przykład dla każdego:
W pierwszym przypadku, należy rozważyć modelowanie biblioteki GUI. Każda kontrola wizualna miałaby (na przykład) funkcję display
, która w ScrollableMixin mogłaby dodać paski przewijania, jeśli jest to wymagane; Wstawka przewijania byłaby klasą bazową dla większości kontroli, które są ponownie spory (ale wszystkie z nich stanowi część „Sterowanie/komponent wizualny/wyświetlanej” klasowej hierarchii.
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>;
W tym przypadku, wszystkie mixin typy zastąpi (na przykład) metodę wyświetlania i przekaże części jej funkcjonalności (specjalistyczna część rysunku) do dekorowanego typu (bazy). do dodania numeru wersji do serializowanych obiektów w aplikacji. Implementacja będzie wyglądać następująco:
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> {};
W tym przypadku zarówno database_query
, jak i user_information
zapisują swoje ustawienia z numerem wersji, ale nie są w żaden sposób w tej samej hierarchii obiektów (nie mają wspólnej podstawy).
Do tego nie służy CRTP. Oczywiście, CRTP obejmuje ogólną strukturę 'class Child: Parent', ale chodzi o to, że klasa bazowa - 'Parent ', wie o klasie potomnej 'Child' (jak? To jest parametr szablonu!) Parent can następnie odwołaj rzeczy zdefiniowane w samym dziecku - np. może stworzyć operatora! = od operatora klasy dziecka ==. –
@Hwalters, niekoniecznie. W powyższym przykładzie, implementacja 'wersji' nie nakłada żadnych ograniczeń na 'T' (to znaczy, że nie musi nic o nim wiedzieć). Dokonano tego celowo. –
utnapistim
Myliłem się; użycie 'Child' do utworzenia instancji' Parent' po prostu jako wygodnego uniquifier dla typu podstawowego ma narzędzie. Ale myślę, że argumentowałeś niewłaściwie - nie nakładanie ograniczeń na "T" jest niewystarczające, by uzasadnić użycie CRTP. W szczególności zmagam się z twoim przykładem; Końcowym rezultatem jest to, że obiekty 'database_query' i' user_information' mają członków version_ i get_version, nawet tych samych typów. Jaką przewagę zapewnia używanie CRTP do ich wstrzykiwania? (Na przykład, klasy bazowe _ są_ różnymi typami, ale w jaki sposób miałoby to znaczenie?) –