1. 定义装饰者模式
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
2. 类图
3. 举例说明
在购买咖啡的时候,我们可以加入各种配料,例如:牛奶(milk)、摩卡(mocha)、奶泡(whip)
你加的东西不一样收取的费用就不一样。
这个时候你肯定会想,我们按照东西给钱就就好了。我要一杯咖啡+牛奶+摩卡 就给这三者相加的钱就好了。 实际生活中情况也是这个样子。
如果使用装饰着模式我们需要怎么做呢?
解决方式:
我们以饮料为主体(超类),然后在运行时以配料来装饰饮料。比方说顾客想要 摩卡(mocha) 和 奶泡(whip) 的浓咖啡(Espresso),那么,要做的是:拿一个浓咖啡(Espresso)对象,以摩卡(Mocha)对象装饰它,以奶泡(whip)对象装饰它.
根据上面类图,我们设计关于 咖啡-配料 的关系类图
4 具体实现代码
由于代码量少,所有函数体就写在.h文件里面了,就建立cpp文件了。
首先实现超类接口:
Beverage.h
Beverage 是一个抽象类,有两个方法: setDescription()和cost()
setDescription() 已经实现,但是cost()必须在子类中实现。
#ifndef BEVERAGE
#define BEVERAGE
#include
class Beverage
{
public:
Beverage(QString str = "Unknow Beverage")
:description(str){}
virtual QString getDescription()
{
return description;
}
virtual double cost(){return 0;}
private:
QString description;
};
#endif // BEVERAGE
所有的配料装饰者都必须重新实现setDescription()方法。
#ifndef CONDIMENTDECORATOR
#define CONDIMENTDECORATOR
#include "Beverage.h"
class CondimentDecorator : public Beverage
{
public:
QString getDescription(){return "";}
};
#endif // CONDIMENTDECORATOR
现在,已经有了基类,让我们开始实现一些饮料吧!先从浓缩咖啡(Espresso)开始。我们需要设置饮料的描述,和它的价格。
Espresso.h
#ifndef ESPRESSO
#define ESPRESSO
#include "Beverage.h"
class Espresso : public Beverage
{
public:
Espresso():Beverage("Espresso"){}
double cost()
{
return 1.99;
}
};
#endif // ESPRESSO
HouseBlend.h
#ifndef HOUSEBLEND
#define HOUSEBLEND
#include "Beverage.h"
class HouseBlend : public Beverage
{
public:
HouseBlend():Beverage("HouseBlend Coffee"){}
double cost()
{
return 0.89;
}
};
#endif // HOUSEBLEND
如果你回头去看看装饰者模式的类图,你会发现我们已经完成了抽闲组件(Beverage),有了具体组件(Espresso、HouseBlend),也有了抽象装饰着(CondimentDecorator)。现在,我们就来实现具体的装饰者,先从摩卡开始。
Mocha.h
#ifndef MOCHA
#define MOCHA
#include "CondimentDecorator.h"
class Mocha : public CondimentDecorator
{
public:
Mocha(Beverage* beve)
{
beverage = beve; // 想办法让被装饰者被记录到实例变量中。这里做法是:把饮料当作构造器的参数,
// 再由构造器将此饮料记录在实例变量中。
}
QString getDescription()
{
return beverage->getDescription()+", Mocha"; // 描述:帮助这杯饮料都有什么材料
}
double cost()
{
return 0.20 + beverage->cost(); // 0.2是摩卡的价钱,加上被装饰的价钱
}
private:
Beverage* beverage;
};
#endif // MOCHA
Whip.h
#ifndef WHIP
#define WHIP
#include "CondimentDecorator.h"
class Whip : public CondimentDecorator
{
public:
Whip(Beverage* beve)
{
beverage = beve;
}
QString getDescription()
{
return beverage->getDescription()+", Whip";
}
double cost()
{
return 0.15 + beverage->cost();
}
private:
Beverage* beverage;
};
#endif // WHIP
现在开始点餐:
main.cpp
#include
#include
#include "HouseBlend.h"
#include "Espresso.h"
#include "Beverage.h"
#include "Mocha.h"
#include "Whip.h"
#include "Soy.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 点一杯 Espresso 什么都不加
Beverage * beverage = new Espresso();
qDebug() << beverage->getDescription() << beverage->cost();
// 点一杯 HouseBlend 加whip 和 Mocha
Beverage * beverage2 = new HouseBlend();
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
qDebug() << beverage2->getDescription() << beverage2->cost();
// 点一杯 HouseBlend 加whip 和 Mocha Soy
Beverage * beverage3 = new HouseBlend();
beverage3 = new Mocha(beverage2);
beverage3 = new Whip(beverage2);
beverage3 = new Soy(beverage2);
qDebug() << beverage3->getDescription() << beverage3->cost();
return a.exec();
}
先来杯浓缩咖啡,
再来杯综合咖啡 + 摩卡 + 奶泡
我们要的结果出来了。接下来总结下。
5. 总结
装饰者模式,我们使用的00原则:
多组合,少继承。
对扩展开发,对修改关闭。
多组合可以很好想,多种配料组合起来的饮料。我们只需要把我们想要的配料,动态的装饰在我们的饮料上。就会得到我们想要的。
对扩展开发,对修改关闭,其实观察者模式也是这样的。我们使用装饰着,相加什么配料,只需要去增加我们配料代码就好了。原来的代码我们不需要修改,随意的扩展 给咖啡加点 醋啊 、酱油啊、盐什么的。
ps: 以上内容学习 Head First设计模式 + 百度百科