装饰者(Decorator)模式
一、问题的提出
有一个咖啡店需要更新订单系统,原来的设计是这样的:
由于现在需要加入各种调料:豆浆(Soy)、摩卡(Mocha)等等,各种调料收取费用不同,所以订单必须考虑这些。
比较容易想到的一种设计方式就是所有的种类都继承自Beverage,但是很明显这样造成了“类爆炸”。
第二种设计方式:在基类中添加一些变量,代表是否加上了调料(豆浆、摩卡等):
这种设计带来的问题:
1、一旦出现新的调料,就要修改代码。
2、调料价钱改变会需要修改代码。
3、如果需要多份调料,怎么办?
4、。。。。
二、认识装饰者模式
由以上两种设计方式遇到的问题有:类数量爆炸、设计死板、基类加入的功能并不适用于所有子类。
现在要用一个新的做法:以Beverage为主体,运行时以调料来“装饰(Decorate)”Beverage。例如:顾客想要摩卡和奶泡深培咖啡,那么步骤如下:
1、拿一个深焙咖啡(DarkRost)对象。
2、以摩卡(Mocha)对象装饰它。
3、以奶泡(Whip)对象装饰它。
4、调用cost方法,并依赖委托(delegate)将调料的价格加上去。
特点:
1、装饰者和被装饰者有相同的超类型。
2、用一个或多个装饰者包装一个对象。
3、装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,已达到特定的目的。
4、对象可以在任何时候被装饰,所以可以在运行时动态的、不限量的使用装饰者来装饰。
定义:装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的解决方案。
类图:
饮料用此类图生成的类图:
三、代码
头文件:
#ifndef DECORATE__H
#define DECORATE__H
#include <string>
#include <iostream>
using namespace std;
class Beverage
{
public:
string description;
Beverage();
string getDescription();
virtual double cost() = 0;
};
class CondimentDecorator : public Beverage
{
public:
virtual string getDescription() = 0;
};
//Espresso饮料
class Espresso : public Beverage
{
public:
Espresso();
double cost();
};
//HouseBlend饮料
class HouseBlend : public Beverage
{
public:
HouseBlend();
double cost();
};
//Mocha装饰者
class Mocha : public CondimentDecorator
{
public:
Beverage* beverage;
Mocha(Beverage* beverage);
string getDescription();
double cost();
};
#endif
cpp文件:
#include "Decorate.h"
Beverage::Beverage()
{
description = "Unknow Beverage";
}
string Beverage::getDescription()
{
return description;
}
Espresso::Espresso()
{
description = "Espresso";
}
double Espresso::cost()
{
return 1.99;
}
HouseBlend::HouseBlend()
{
description = "House Blend Coffee";
}
double HouseBlend::cost()
{
return 0.89;
}
Mocha::Mocha(Beverage* beverage)
{
this->beverage = beverage;
}
string Mocha::getDescription()
{
return beverage->getDescription() + ", Mocha";
}
double Mocha::cost()
{
return 0.20 + beverage->cost();
}
四、总结
1、组合和委托可以用在运行时动态添加新行为
2、除了继承,装饰者可以扩展行为
3、装饰者会导师设计中出现许多小对象(用来装饰),如果过度使用,程序会变得复杂。
4、装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。