装饰模式
1.1 分类
(对象)结构型
1.2 提出问题
饮料店订单系统,饮料有多种,并且可以选择加牛奶、冰激凌、巧克力等配料。
1.3 解决方案
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。找出基本组件和可选层次。
1.4 实现类图
- 组件(Component)声明封装器和被封装对象的公用接口。
- 具体组件(Concrete Component)类是被封装对象所属的类。 它定义了基础行为,但装饰类可以改变这些行为。
- 基础装饰(Base Decorator)类拥有指向被封装对象的引用成员变量。装饰基类会将所有操作委派给被封装的对象。
- 具体装饰类(Concrete Decorators)定义了可动态添加到组件的额外行为。具体装饰类会重写装饰基类的方法,并在调用父类方法之前或之后进行额外的行为。
- 客户端(Client)可以使用多层装饰来封装部件,只要它能使用通用接口与所有对象互动即可。
1.5 示例代码
#include <iostream>
#include <string>
//接口:具体被包裹的类,以及装饰类
class Beverage {
public:
virtual ~Beverage() {}
virtual std::string Operation() const = 0;
};
//具体的被装饰者
class Americano : public Beverage {
public:
~Americano() {}
std::string Operation() const {
return "美式咖啡";
}
};
//装饰
//基础部分(可能包括额外部分)
class Ingredient : public Beverage {
protected:
Beverage* m_beverage;
public:
~Ingredient() {}
Ingredient(Beverage* beverage):m_beverage(beverage) {}
std::string Operation() const override {
return m_beverage->Operation();//核心代码
}
};
//额外部分(需要委托基类,完成基础部分)
class Whip : public Ingredient {
public:
~Whip() {}
Whip(Beverage* beverage) : Ingredient(beverage) {}
std::string Operation() const override {
//在基类的operation之前,之后都可以增加额外的操作
return "奶昔("+Ingredient::Operation()+")";
}
};
class Mocha : public Ingredient {
public:
~Mocha() {}
Mocha(Beverage* beverage) : Ingredient(beverage) {}
std::string Operation() const override {
//在基类的operation之前,之后都可以增加额外的操作
return "摩卡(" + Ingredient::Operation() + ")";
}
};
void ClientCode(Beverage* beverage) {
std::cout << "执行结果:" << beverage->Operation();
}
int main()
{
std::cout << "来一杯普通美式咖啡:\n";
Beverage* americano = new Americano();
ClientCode(americano);
std::cout << "\n\n";
std::cout << "来一杯双份摩卡+奶昔的美式咖啡:\n";
Beverage* whip1 = new Whip(americano);
Beverage* mocha1 = new Mocha(whip1);
Beverage* mocha2 = new Mocha(mocha1);
ClientCode(mocha2);
delete americano;
delete whip1;
delete mocha1;
delete mocha2;
}
1.6 举个栗子
使用装饰模式能够对敏感数据进行压缩和加密:
1.7 总结
1.7.1 优点
- 无需创建新子类即可扩展对象的行为。
- 可以在运行时添加或删除对象的功能。
- 可以用多个装饰封装对象来组合几种行为。
- 单一职责原则。将实现了许多不同行为的一个大类拆分为多个较小的类。
1.7.2 缺点
- 在封装器栈中删除特定封装器比较困难。
- 实现行为不受装饰栈顺序影响的装饰比较困难。
- 各层的初始化配置代码看上去可能会很糟糕。