设计模式系列(三)装饰者模式(Decorator Pattern)
装饰者模式是指动态地将责任附加到对象上,而不必改变原类文件。对于扩展功能,装饰者模式提供了继承以外的另一种扩展对象功能的方式,不过装饰者模式比继承更加具有弹性。装饰者模式允许行为可以被扩展,而无须修改现有的代码。它使用组合和委托的方式来实现。
装饰者模式的设计原则是:开放-关闭原则,即对扩展开放,对修改关闭以及多用组合,少用继承的原则。
装饰者模式通常由一些基本的组件和装饰者组成,它的特点主要是:
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)。
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
(5) 设计者可以通过将各种各样的装饰者进行排列组合,得到不同的装饰结果,从而实现各种功能扩展。
以下情况使用Decorator模式:
(1) 需要扩展一个类的功能,或给一个类添加附加职责。
(2) 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
(3) 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
(4) 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
装饰者模式的优点:
(1)比继承具有更大的弹性和灵活性。
(2)设计者可以通过将各种各样的装饰者进行排列组合,得到不同的装饰结果,从而实现各种功能扩展。
(3)装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
装饰者模式的缺点:
(1) 装饰者模式由于需要一些组件和装饰者,所以会引入很多小类,从而增加程序的复杂性,让程序变得难以理解。
(2) 装饰者模式在实例化组件时比较复杂,需要一步步从基本功能到装饰者功能一层层包装,增加了代码的复杂性,不过这个问题可以使用工厂模式或生成器来解决。
(3) 装饰者模式是针对抽象组件类型编程,所以要清楚自己的设计是需要对抽象还是具体。
在JAVA中,装饰者模式使用也很多,最为常见的就是IO流的功能实现就是通过装饰者模式实现的。在装饰模式中的各个角色有:
(1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
(2)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
(3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
(4)具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
下面我们来看一个例子,这个例子包含三个文件,依次是:DecoratorPattern.h文件、DecoratorPattern.cpp文件以及DecoratorPatternTest.cpp文件。
// 装饰者模式
#ifndef DECORATOR
#define DECORATOR
#include <iostream>
#include <string>
#include <iomanip>
using std::string;
using std::cout;
using std::endl;
using std::fixed;
using std::setprecision;
typedef enum size{ TALL, GRANDE, VENTI }coffeeSize;
// 所有类的超类
class Beverage
{
public:
Beverage()
{
description = "Unknown Beverage";
cs = GRANDE;
}
virtual ~Beverage(){}
virtual string getDescription();
virtual void setSize(coffeeSize cs);
virtual coffeeSize getSize();
virtual double cost() = 0;
string description;
private:
coffeeSize cs;
};
// 装饰者的抽象类
class CondimentDecorator : public Beverage
{
public:
CondimentDecorator(){}
virtual ~CondimentDecorator(){}
virtual string getDescription() = 0;
};
// 被装饰者具体类1
class HouseBlend : public Beverage
{
public:
HouseBlend()
{
description = "House Blend Coffee";
}
double cost();
};
// 被装饰者具体类2
class DarkRoast : public Beverage
{
public:
DarkRoast()
{
description = "Dark Roast Coffee";
}
double cost();
};
// 被装饰者具体类3
class Espresso : public Beverage
{
public:
Espresso()
{
description = "Espresso";
}
double cost();
};
// 被装饰者具体类4
class Decaf : public Beverage
{
public:
Decaf()
{
description = "Decaf Coffee";
}
double cost();
};
// 装饰者具体类1
class Milk : public CondimentDecorator
{
public:
Milk(Beverage* beverage) {
this->beverage = beverage;
}
~Milk()
{
cout << "调用析构函数" << endl;
if (beverage != NULL)
{
delete beverage;
beverage = NULL;
}
}
string getDescription();
double cost();
Beverage* beverage;
};
// 装饰者具体类2
class Mocha : public CondimentDecorator
{
public:
Mocha(Beverage* beverage) {
this->beverage = beverage;
}
~Mocha()
{
cout << "调用析构函数" << endl;
if (beverage != NULL)
{
delete beverage;
beverage = NULL;
}
}
string getDescription();
double cost();
Beverage* beverage;
};
// 装饰者具体类3
class Soy : public CondimentDecorator
{
public:
Soy(Beverage* beverage) {
this->beverage = beverage;
}
~Soy()
{
cout << "调用析构函数" << endl;
if (beverage != NULL)
{
delete beverage;
beverage = NULL;
}
}
string getDescription();
double cost();
Beverage* beverage;
};
// 装饰者具体类4
class Whip : public CondimentDecorator
{
public:
Whip(Beverage* beverage) {
this->beverage = beverage;
}
~Whip()
{
cout << "调用析构函数" << endl;
if (beverage != NULL)
{
delete beverage;
beverage = NULL;
}
}
string getDescription();
double cost();
Beverage* beverage;
};
#endif
#include"DecoratorPattern.h"
// 所有类的超类
stringBeverage::getDescription()
{
return description;
}
void Beverage::setSize(coffeeSizecs)
{
this->cs = cs;
}
coffeeSizeBeverage::getSize()
{
return cs;
}
// 被装饰者具体类1
doubleHouseBlend::cost()
{
if (getSize() == TALL)
{
return 0.89;
}
else if (getSize() == GRANDE)
{
return 0.94;
}
else
{
return 0.99;
}
}
// 被装饰者具体类2
doubleDarkRoast::cost()
{
if (getSize() == TALL)
{
return 0.99;
}
else if (getSize() == GRANDE)
{
return 1.04;
}
else
{
return 1.09;
}
}
// 被装饰者具体类3
doubleEspresso::cost()
{
if (getSize() == TALL)
{
return 1.99;
}
else if (getSize() == GRANDE)
{
return 2.04;
}
else
{
return 2.09;
}
}
// 被装饰者具体类4
double Decaf::cost()
{
if (getSize() == TALL)
{
return 1.05;
}
else if (getSize() == GRANDE)
{
return 1.09;
}
else
{
return 1.14;
}
}
// 装饰者具体类1
stringMilk::getDescription()
{
return beverage->getDescription() +", Milk";
}
double Milk::cost()
{
if (beverage->getSize() == TALL)
{
return 0.10 +beverage->cost();
}
else if (beverage->getSize() ==GRANDE)
{
return 0.15 + beverage->cost();
}
else
{
return 0.20 +beverage->cost();
}
}
// 装饰者具体类2
stringMocha::getDescription()
{
return beverage->getDescription() +", Mocha";
}
double Mocha::cost()
{
if (beverage->getSize() == TALL)
{
return 0.20 +beverage->cost();
}
else if (beverage->getSize() ==GRANDE)
{
return 0.25 +beverage->cost();
}
else
{
return 0.30 +beverage->cost();
}
}
// 装饰者具体类3
stringSoy::getDescription()
{
return beverage->getDescription() +", Soy";
}
double Soy::cost()
{
if (beverage->getSize() == TALL)
{
return 0.15 +beverage->cost();
}
else if (beverage->getSize() ==GRANDE)
{
return 0.20 +beverage->cost();
}
else
{
return 0.25 +beverage->cost();
}
}
// 装饰者具体类4
stringWhip::getDescription()
{
return beverage->getDescription() +", Whip";
}
double Whip::cost()
{
if (beverage->getSize() == TALL)
{
return 0.10 +beverage->cost();
}
else if (beverage->getSize() ==GRANDE)
{
return 0.15 +beverage->cost();
}
else
{
return 0.20 +beverage->cost();
}
}
#include "DecoratorPattern.h"
void main()
{
cout << "---------------------------------------------" << endl;
Beverage* beverage = new Espresso();
if (beverage->getSize() == TALL)
{
cout << "Little cup: " << endl;
}
else if (beverage->getSize() == GRANDE)
{
cout << "Middle cup: " << endl;
}
else
{
cout << "Big cup: " << endl;
}
cout << fixed << setprecision(2) << beverage->getDescription()
<< " $" << beverage->cost() << endl;
cout << "---------------------------------------------" << endl;
Beverage* beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
if (beverage2->getSize() == TALL)
{
cout << "Little cup: " << endl;
}
else if (beverage2->getSize() == GRANDE)
{
cout << "Middle cup: " << endl;
}
else
{
cout << "Big cup: " << endl;
}
cout << fixed << setprecision(2) << beverage2->getDescription()
<< " $" << beverage2->cost() << endl;
cout << "---------------------------------------------" << endl;
Beverage* beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
if (beverage3->getSize() == TALL)
{
cout << "Little cup: " << endl;
}
else if (beverage3->getSize() == GRANDE)
{
cout << "Middle cup: " << endl;
}
else
{
cout << "Big cup: " << endl;
}
cout << fixed << setprecision(2) << beverage3->getDescription()
<< " $" << beverage3->cost() << endl;
cout << "---------------------------------------------" << endl;
Beverage* beverage4 = new DarkRoast();
beverage4->setSize(TALL);
beverage4 = new Mocha(beverage4);
beverage4->setSize(TALL);
beverage4 = new Mocha(beverage4);
beverage4->setSize(TALL);
beverage4 = new Whip(beverage4);
beverage4->setSize(TALL);
if (beverage4->getSize() == TALL)
{
cout << "Little cup: " << endl;
}
else if (beverage4->getSize() == GRANDE)
{
cout << "Middle cup: " << endl;
}
else
{
cout << "Big cup: " << endl;
}
cout << fixed << setprecision(2) << beverage4->getDescription()
<< " $" << beverage4->cost() << endl;
cout << "---------------------------------------------" << endl;
Beverage* beverage5 = new HouseBlend();
beverage5->setSize(VENTI);
beverage5 = new Soy(beverage5);
beverage5->setSize(VENTI);
beverage5 = new Mocha(beverage5);
beverage5->setSize(VENTI);
beverage5 = new Whip(beverage5);
beverage5->setSize(VENTI);
if (beverage5->getSize() == TALL)
{
cout << "Little cup: " << endl;
}
else if (beverage5->getSize() == GRANDE)
{
cout << "Middle cup: " << endl;
}
else
{
cout << "Big cup: " << endl;
}
cout << fixed << setprecision(2) << beverage5->getDescription()
<< " $" << beverage5->cost() << endl;
cout << "---------------------------------------------" << endl;
delete beverage;
delete beverage2;
delete beverage3;
delete beverage4;
delete beverage5;
beverage = NULL;
beverage2 = NULL;
beverage3 = NULL;
beverage4 = NULL;
beverage5 = NULL;
}
该例的运行结果如图1所示,一定要注意C++中指针的内存释放问题!!!
图1 运行结果
该例的UML类图如图2所示。
图2 UML类图
该例主要是一个咖啡店的售卖系统,有最基本的咖啡抽象组件--Beverage,这个组件为被装饰者和装饰者提供了统一的接口。具体的组件是图2中左上角的四个组件,这四个组件是具体的咖啡类型,它们有不同的名称和价格,这四个组件就是被装饰者。大家都知道,每种咖啡可以加不同的配料,如牛奶、奶泡、豆浆、摩卡等,这些配料就是具体的装饰者,而它们的共同父类就是一个继承于Beverage抽象组件的一个抽象装饰者,它为装饰者提供统一的接口。正如喝咖啡可以随意组合配料一样,这里可以通过排列组合具体的装饰者实现不同的咖啡配料,即所谓的扩展功能,如牛奶豆浆咖啡或者豆浆摩卡咖啡,甚至于牛奶豆浆摩卡奶泡咖啡,具体是什么咖啡都可以根据顾客的选择进行动态变化。这里用到了组合和委托,组合在于四个装饰者中都包含一个指向被装饰者的指针,委托在于每一次计算价格的时候,装饰者委托这个指针动态地进行价格计算,这里也用到了多态的特性。
最后需要注意的是,本例通过一个枚举类型(图2右上角)表示咖啡的不同大小的杯,在咖啡抽象组件--Beverage中也定义了set和get函数来设置和获取咖啡的大小杯,并且在测试函数中也用到了很多特性,大家可以慢慢理解一下,有问题的可以留言。