我正在研究mixins(用C ++编写)。我阅读了一些关于mixins的文章,并在C ++中发现了两种不同的“近似”mixins模式。
模式1:
template<class Base>
struct Mixin1 : public Base {
};
template<class Base>
struct Mixin2 : public Base {
};
struct MyType {
};
typedef Mixin2<Mixin1<MyType>> MyTypeWithMixins;
模式2 :(可称为CRTP)
template<class T>
struct Mixin1 {
};
template<class T>
struct Mixin2 {
};
struct MyType {
};
struct MyTypeWithMixins :
public MyType,
public Mixin1<MyTypeWithMixins>,
public Mixin2<MyTypeWithMixins> {
};
它们实际上是等同的吗?我想知道模式之间的实际差异。
答案 0 :(得分:8)
区别在于可见度。在第一种模式中,MyType
的成员直接可见并可由mixin使用,无需任何转换,Mixin1
的成员对Mixin2
可见。如果MyType
想要从mixin访问成员,则需要投射this
,并且没有一种安全的方法。
在第二种模式中,类型和mixin之间没有自动可见性,但mixin可以安全轻松地将this
转换为MyTypeWithMixins
,从而访问该类型和其他成员混入。 (MyType
也可以,如果您也应用CRTP。)
因此,它归结为方便性与灵活性。如果你的mixins纯粹是从类型中访问服务,并且没有自己的兄弟依赖,那么第一个模式就很好而且简单明了。如果mixin依赖于类型或其他mixin提供的服务,那么你或多或少会被迫使用第二种模式。
答案 1 :(得分:7)
它们实际上是等同的吗?我想知道模式之间的实际差异。
它们在概念上是不同的。
对于第一个模式,您有装饰器(透明地)在核心功能类上运行,每个装饰器都将它们自己的扭曲/特化添加到现有实现中。
第一个模式模型的关系是&#34;是-a&#34; (MyTypeWithMixins
是Mixin1<MyType>
专精,Mixin1<MyType>
是MyType
专业化。
当您在严格的界面中实现功能时,这是一种很好的方法(因为所有类型将实现相同的界面)。
对于第二种模式,您将功能部件用作实现细节(可能在不同的,不相关的类中)。
这里建模的关系是&#34;是根据&#34; (MyTypeWithMixins
是一个MyType
专门化,是根据 Mixin1
和Mixin2
功能实现的。在许多CRTP实现中,CRTP模板化基础被继承为私有或受保护。
当您在不同的,不相关的组件(即不具有相同的接口)上实现通用功能时,这是一种很好的方法。这是因为从Mixin1继承的两个类 not 具有相同的基类。
为每个人提供一个具体的例子:
对于第一种情况,请考虑GUI库的建模。每个可视控件都有一个(例如)display
函数,如果需要,它可以在ScrollableMixin中添加滚动条;滚动条mixin将是大多数可重新调整大小的控件的基类(但它们都是&#34;控件/可视组件/可显示&#34;类层次结构的一部分。
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>;
在这种情况下,所有mixin类型都会覆盖(例如)显示方法,并将其部分功能(专用绘图部分)委托给装饰类型(基础)。
对于第二种情况,考虑一种情况,即实现内部系统以向应用程序中的序列化对象添加版本号。实现将如下所示:
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> {};
在这种情况下,database_query
和user_information
都会将其设置存储为版本号,但它们不在同一对象层次结构中(它们没有共同的基础)