Are they equivalent practically? I'd like to know practical difference between the patterns.
They are different conceptually.
For the first pattern, you have decorators going (transparently) over a core functionality class, each adding their own twist/specialization to an existing implementation.
The relationship the first pattern models is "is-a" (MyTypeWithMixins
is a Mixin1<MyType>
specialization, Mixin1<MyType>
is a MyType
specialization).
This is a good approach when you are implementing functionality within a rigid interface (as all types will implement the same interface).
For the second pattern, you have functionality parts used as implementation details (possibly within different, unrelated classes).
The relationship modeled here is "is implemented in terms of" (MyTypeWithMixins
is a MyType
specialization, implemented in terms of Mixin1
and Mixin2
functionality). In many CRTP implementation, the CRTP templated base is inherited as private or protected.
This is a good approach when you are implementing common functionality accross different, unrelated components (i.e. not with the same interface). This is because two classes inheriting from Mixin1 will not have the same base class.
To provide a concrete example for each:
For the first case, consider the modeling of a GUI library. Each visual control would have a (for example) display
function, which in a ScrollableMixin would add scroll-bars, if required; The scrollbars mixin would be a base class for most controls that are re-sizable (but all of them a part of the "control/visual component/displayable" class hierarchy.
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 this case, all mixin types would override (for example) a display method, and delegate parts of it's functionality (the specialized drawing part) to the decorated type (the base).
For the second case, consider a case when you would implement an internal system for adding a version number to serialized objects within the application. The implementation would look like this:
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 this case, both database_query
and user_information
store their settings with a version number, but they are in no way in the same object hierarchy (they don't have a common base).