前言
由于设计模式章节将贴出大量伪代码,为了压缩代码长度方便大家查看,因此代码段中与设计模式无关的代码,如构造函数、或是成员函数public、private、protect属性等均未写出。文章中若有说明不清晰或是有歧义、错误的地方欢迎大家评论指正,谢谢大家。
一、定义
装饰器模式(Decorator Pattern)的定义:装饰器模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
二、定义的白话叙述
上述装饰器模式的定义可能有些晦涩,用白话来说就是在不改变一个类原有代码的基础上扩展该类的功能。
例如我们现在有下面一个getChar类,给入一个数字,输出一个字符。
class getChar
{
char numToChar(int pos)
{
return pos;
}
};
现在我们想对numToChar进行改造,实现输入1-26数字,输出对应坐标的小写字母。如果输入的数字不在范围内,返回空字符。常规的做法有2种:
1、改造现有getChar类的接口。
2、继承将numToChar设置为虚函数,继承getChar类实现一个新的类,实现numToChar接口。
而使用装饰器模式的做法是:实现一个新的类,类内包含getChar类的对象,并实现一个numToChar接口。这样用户直接将代码如下:
class getChar
{
public:
char numToChar(int pos)
{
return pos;
}
};
class getEng
{
public:
getEng(getChar* ins)
{
m_ins = ins;
}
char numToChar(int pos)
{
if(pos < 1 || pos > 26 || m_ins == nullptr)
{
return '';
}
else
{
//'a'对应的十进制数字为97
return m_ins->numToChar(97 + pos - 1);
}
}
private;
getChar* m_ins;
}
三、一个适用装饰器模式的场景
生活中大家都会喝各种各样的饮料,以珍珠奶茶为例,计算一杯珍珠奶茶价格的场景就恰好试用装饰器模式。价目表如下:
例如一杯基础的奶茶10块钱,添加一份糖加1块钱,添加一份牛奶加2块钱,添加一份珍珠加2块钱,总计15元。下面我们使用装饰器模式来还原这样的场景。
四、使用装饰器模式实现奶茶价格的计算代码
1、代码功能设计
1、抽象饮料类Beverage,包含纯虚函数getprice获取奶茶价格接口
2、奶茶饮料类FruitBeverage ,继承抽象饮料类,实现getprice接口,返回一杯基础奶茶的价格
3、抽象装饰器类,继承奶茶饮料类FruitBeverage ,实现getprice接口
4、加料类SugarDec 、MilkDesc 、BubbleDesc ,继承FruitBeverage 实现getprice,返回各种加料的价格。
//抽象饮料类
class Beverage
{
public:
virtual int getprice() = 0; //获取价格
public:
virtual ~Beverage() {}
};
//奶茶饮料类
class FruitBeverage : public Beverage
{
public:
virtual int getprice()
{
return 10; //一杯单纯的奶茶饮料,售价为10元
}
};
//抽象的装饰器类
class Decorator :public Beverage
{
public:
Decorator(Beverage* tmpbvg) :m_pbvg(tmpbvg) {} //构造函数
virtual int getprice()
{
return m_pbvg->getprice();
}
private:
Beverage* m_pbvg;
};
//具体的“砂糖”装饰器类
class SugarDec :public Decorator
{
public:
SugarDec(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
virtual int getprice()
{
return Decorator::getprice() + 1; //额外加多1元,要调用父类的getprice方法以把以往的价格增加进来
}
};
//具体的“牛奶”装饰器类
class MilkDesc :public Decorator
{
public:
MilkDesc(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
virtual int getprice()
{
return Decorator::getprice() + 2; //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
}
};
//具体的“珍珠”装饰器类
class BubbleDesc :public Decorator
{
public:
BubbleDesc(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
virtual int getprice()
{
return Decorator::getprice() + 2; //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
}
};
2、装饰器代码的使用
下面代码为使用装饰器模式代码计算珍珠奶茶价格的步骤,一开始创建了pfuit基础奶茶类,随后依次创建了添加珍珠的类,添加白糖的类,并且在每次创建新类对象时,都传入了上一个传入的对象。最后调用最后一个创建的pfruit_addbubb_addsugar 对象的getprice方法。由于装饰器类中的每一个实现,都保存了构造函数传入的类对象。所以当调用最后一个对象的getprice时,会递归的依次调用下去每一个对象的getprice方法,最终获取到一杯添加白糖和牛奶的奶茶价格。
//创建一杯单纯的水果饮料,价格10元:
Beverage* pfruit = new FruitBeverage();
//向饮料中增加珍珠,价格多加了2元
Decorator *pfruit_addbubb = new BubbleDesc(pfruit);
//再向饮料中增加砂糖,价格又加多了1元
Decorator* pfruit_addbubb_addsugar = new SugarDec(pfruit_addbubb);
//输出最终的价格
cout << "加了珍珠又加了砂糖的水果饮料最终价格是:" << \
pfruit_addbubb_addsugar->getprice() << "元人民币" << endl;
//释放资源
delete pfruit_addbubb_addsugar;
delete pfruit_addbubb;
delete pfruit;