装饰器模式——现代C++设计模式
当已经存在一个类,想要扩展他的功能,该怎么办呢?继承?由于单一职责原则,需要将扩展的功能单独分开
使用场景
装饰器模式能够在不修改原始类型(不违反开闭原则)、不产生大量派生类型的情况下扩展现有类的功能。
通常通过组合的形式,包括了动态组合和静态组合
动态装饰器实现
#include<iostream>
#include<vector>
using namespace std;
struct Shape {
virtual string str() const = 0;
};
struct Circle :Shape{ //继承的类型函数
float radius;
explicit Circle(const float radius):radius(radius){}
string str()const override
{
return "A circle of radius";
}
};
struct ColoredShape :Shape { //动态装饰器
Shape& shape; //将要装饰的引用
string color;
ColoredShape(Shape& shape, const string& color) //初始化时设置将要装饰的对象
:shape{ shape }, color{ color } {}
string str()const override
{
return shape.str() + " has the color " + color;
}
void make_dark() {
if (constexpr auto dark = "dark"; !color.starts_with(dark))
color.insert(0, dark);
//constexpr是c++11新特性,可以将运算放在编译阶段,而不是运行阶段
//starts_with判断字符串是否为右侧子字符串开头
}
};
int main() {
Circle circle{ 1 };//生成对象
ColoredShape red{ circle,"red" };
cout << red.str()<<endl;
red.make_dark();
cout << red.str()<<endl;
return 0;
}
在上边这个例子中,装饰器中包括了原有类的引用,通过传入引用的方法进行是功能扩展,相应的也可以定义用来扩展其他功能的装饰器:如Sizedshape用来修改形状
静态装饰器实现——Mixin继承
#include<iostream>
#include<vector>
using namespace std;
struct Shape {
virtual string str() const = 0;
};
struct Circle :Shape{ //继承的类型函数
float radius;
explicit Circle(const float radius):radius(radius){}
string str()const override
{
return "A circle of radius ";
}
};
//继承自模板参数
template<typename T>struct ColoredShape :T
{
static_assert(is_base_of_v<Shape, T>, "Template argument must be a Shape");
//判断基类是否为Shape
string color;
string str()const override {
return T::str() + " has the color:" + color;
}
template<class ...Args> //通过模板实现任意参数传递
ColoredShape(string data, Args ...args) :T(forward<Args>(args)...), color{ data } {}
};
template<typename T>struct TransparentShape :T
{
static_assert(is_base_of_v<Shape, T>, "Template argument must be a Shape");
string Transparent;
string str()const override {
return T::str() + " has the Transparent:" + Transparent;
}
template<class ...Args>
TransparentShape(string data, Args ...args) :T(forward<Args>(args)...), Transparent{ data } {}
};
int main() {
Circle circle{ 1 };//生成对象
TransparentShape<ColoredShape<Circle>> square{ "1","Blue",3}; //Arg性质能够快速赋值
cout<<square.str();
return 0;
}
输出
A circle of radius has the color;Blue has the Transparent:1
可以观察到,静态组合意味着对象及其强化功能是在编译阶段使用模板组合而成的,也就是需要模板继承基类。
例子实现了Minix继承:Mixin继承中,类继承自它的模板参数,相比于普通继承,状态空间会更小,在编译时组合装饰器。
还通过模板实现了接受任意数量的参数。
由于是继承,所以动态装饰器能够访问被装饰对象的所有成员。
总结
-
动态装饰器可以存储对被装饰对象的引用(如果需要,甚至可以存储整个值!)并
提供动态(运行时)可组合性,但代价是无法访问底层对象自己的成员。 -
静态装饰器使用mixin继承(从模板参数继承)在编译时组合装饰器。这虽然失去
了运行时的灵活性(无法重新组合对象),但允许访问底层对象的成员。这些对象也
可以通过构造函数转发进行完全初始化。