装饰者模式是指动态地将责任附加到对象上,而不必改变原类文件。对于扩展功能,装饰者模式提供了继承以外的另一种扩展对象功能的方式,不过装饰者模式比继承更加具有弹性。装饰者模式允许行为可以被扩展,而无须修改现有的代码。它使用组合和委托的方式来实现。
装饰者模式的设计原则是:开放-关闭原则,即对扩展开放,对修改关闭以及多用组合,少用继承的原则。
装饰者模式通常由一些基本的组件和装饰者组成,它的特点主要是:
(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函数来设置和获取咖啡的大小杯,并且在测试函数中也用到了很多特性,大家可以慢慢理解一下,有问题的可以留言。