upx*_*xuk 13 c++ design-patterns mixins crtp
我正在研究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;
Run Code Online (Sandbox Code Playgroud)
模式2 :(可称为CRTP)
template<class T>
struct Mixin1 {
};
template<class T>
struct Mixin2 {
};
struct MyType {
};
struct MyTypeWithMixins :
public MyType,
public Mixin1<MyTypeWithMixins>,
public Mixin2<MyTypeWithMixins> {
};
Run Code Online (Sandbox Code Playgroud)
它们实际上相当吗?我想知道模式之间的实际差异.
不同之处在于可见度.在第一种模式中,其MyType成员直接可见并可由mixin使用,无需任何铸造,并且其Mixin1成员可见Mixin2.如果MyType想要从mixin访问成员,它需要强制转换this,并且没有一个很好的方法可以安全地这样做.
在第二种模式中,在类型和混合物之间没有自动可见性,但是混合物可以安全且容易地投射this到MyTypeWithMixins该类型和其他混合物的成员上.(MyType也可以,如果你也应用了CRTP.)
所以它归结为方便性和灵活性.如果你的mixins纯粹是从类型中访问服务,并且没有自己的兄弟依赖,那么第一个模式就很好而且简单明了.如果mixin依赖于类型或其他mixin提供的服务,那么您或多或少会被迫使用第二种模式.
它们实际上相当吗?我想知道模式之间的实际差异.
它们在概念上是不同的.
对于第一个模式,您有装饰器(透明地)在核心功能类上运行,每个装饰器都将自己的扭曲/特化添加到现有实现中.
第一种模式模型的关系是"is-a"(MyTypeWithMixins是一种Mixin1<MyType>专业化,Mixin1<MyType>是一种MyType专业化).
当您在刚性接口中实现功能时,这是一种很好的方法(因为所有类型都将实现相同的接口).
对于第二种模式,您将功能部件用作实现细节(可能在不同的,不相关的类中).
这里建模的关系以"来讲是实现"(MyTypeWithMixins是一个MyType专业化,在以下方面实现 Mixin1和Mixin2功能).在许多CRTP实现中,CRTP模板化基础被继承为私有或受保护.
当您在不同的,不相关的组件(即不具有相同的接口)上实现通用功能时,这是一种很好的方法.这是因为从Mixin1继承的两个类将不具有相同的基类.
为每个提供一个具体的例子:
对于第一种情况,请考虑GUI库的建模.每个可视控件都有一个(例如)display函数,如果需要,它可以在ScrollableMixin中添加滚动条; 滚动条mixin将是大多数可重新调整大小的控件的基类(但它们都是"控制/可视组件/可显示"类层次结构的一部分.
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>;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,所有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> {};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,无论是database_query和user_information存储他们设置了版本号,但他们是在同一个对象的层次结构没有办法(他们没有一个共同的基础).
| 归档时间: |
|
| 查看次数: |
1715 次 |
| 最近记录: |