目录
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时保持原有的结构不变。装饰模式通过动态地将责任附加到对象上来实现这一点。这种类型的动态关联是替代使用继承的灵活替代方案。
装饰模式的关键概念
- Component(组件)接口:定义了被装饰对象的基本行为。
- Concrete Component(具体组件):实现了Component接口,是被装饰的对象。
- Decorator(装饰器)类:也是Component接口的实现者,它可以装饰具体的组件,并在原有的行为上添加新的行为。
- Concrete Decorator(具体装饰器)类:继承自Decorator,提供了具体的装饰功能。
装饰模式的优点
- 灵活性:可以在运行时动态地添加职责。
- 扩展性:可以通过组合不同的装饰器来获得不同的效果。
- 透明性:装饰器和具体组件都实现了同一个接口,使得客户端可以一致地处理它们。
装饰模式的缺点
- 过度使用:可能导致类的数量增加,使得系统变得复杂。
- 调试难度:装饰器模式可能导致对象创建的逻辑变得复杂,使得调试更加困难。
例子:咖啡饮品
假设我们正在设计一个咖啡店的菜单系统,其中包含基础的咖啡饮品和各种额外的配料。我们希望能够在运行时动态地添加配料,而不改变原有饮品的基本结构。
步骤 1:定义Component接口
c
深色版本
1#include <iostream>
2#include <string>
3
4class Beverage {
5public:
6 virtual ~Beverage() {}
7 virtual std::string getDescription() const = 0;
8 virtual double cost() const = 0;
9};
10
11class Espresso : public Beverage {
12public:
13 std::string getDescription() const override {
14 return "Espresso";
15 }
16 double cost() const override {
17 return 1.99;
18 }
19};
20
21class HouseBlend : public Beverage {
22public:
23 std::string getDescription() const override {
24 return "House Blend Coffee";
25 }
26 double cost() const override {
27 return 0.89;
28 }
29};
30
31class Decorator : public Beverage {
32protected:
33 Beverage *beverage;
34public:
35 Decorator(Beverage *beverage) : beverage(beverage) {}
36 std::string getDescription() const override {
37 return beverage->getDescription();
38 }
39 double cost() const override {
40 return beverage->cost();
41 }
42};
43
44class Mocha : public Decorator {
45public:
46 Mocha(Beverage *beverage) : Decorator(beverage) {}
47 std::string getDescription() const override {
48 return Decorator::getDescription() + ", Mocha";
49 }
50 double cost() const override {
51 return Decorator::cost() + 0.20;
52 }
53};
54
55class Soy : public Decorator {
56public:
57 Soy(Beverage *beverage) : Decorator(beverage) {}
58 std::string getDescription() const override {
59 return Decorator::getDescription() + ", Soy";
60 }
61 double cost() const override {
62 return Decorator::cost() + 0.15;
63 }
64};
步骤 2:创建装饰结构
c
深色版本
1int main() {
2 // 创建基础饮品
3 Beverage *houseBlend = new HouseBlend();
4 std::cout << houseBlend->getDescription() << " $" << houseBlend->cost() << std::endl;
5
6 // 添加配料
7 Beverage *houseBlendWithSoy = new Soy(houseBlend);
8 std::cout << houseBlendWithSoy->getDescription() << " $" << houseBlendWithSoy->cost() << std::endl;
9
10 // 添加更多配料
11 Beverage *houseBlendWithMochaAndSoy = new Mocha(houseBlendWithSoy);
12 std::cout << houseBlendWithMochaAndSoy->getDescription() << " $" << houseBlendWithMochaAndSoy->cost() << std::endl;
13
14 // 创建另一种饮品
15 Beverage *espresso = new Espresso();
16 std::cout << espresso->getDescription() << " $" << espresso->cost() << std::endl;
17
18 // 添加配料
19 Beverage *espressoWithMocha = new Mocha(espresso);
20 std::cout << espressoWithMocha->getDescription() << " $" << espressoWithMocha->cost() << std::endl;
21
22 // 清理
23 delete houseBlendWithMochaAndSoy;
24 delete houseBlendWithSoy;
25 delete houseBlend;
26 delete espressoWithMocha;
27 delete espresso;
28
29 return 0;
30}
输出结果
plaintext
深色版本
1House Blend Coffee $0.89
2House Blend Coffee, Soy $1.04
3House Blend Coffee, Soy, Mocha $1.24
4Espresso $1.99
5Espresso, Mocha $2.19
说明
在这个例子中:
Beverage
是抽象基类,定义了饮品的基本行为。Espresso
和HouseBlend
是具体组件,分别代表不同的基础饮品。Decorator
是抽象装饰器,继承自Beverage
,并提供了一个指向具体组件的引用。Mocha
和Soy
是具体装饰器,它们继承自Decorator
,并在原有的饮品上添加新的配料。
优点
- 灵活性:可以在运行时动态地添加配料,而不需要改变原有饮品的结构。
- 透明性:所有的饮品(基础饮品和装饰后的饮品)都实现了相同的接口,使得客户端可以一致地处理它们。
总结
装饰模式通过动态地添加职责来扩展对象的功能,而无需通过继承来改变原有的结构。这种模式非常适合需要在运行时灵活地添加功能的场景。