「软件设计模式」装饰者模式(Decorator)
深入解析装饰者模式:动态扩展功能的艺术(C++实现)
一、模式思想与应用场景
1.1 模式定义
装饰者模式(Decorator Pattern)是一种结构型设计模式,它通过将对象放入包含行为的特殊封装对象中,动态地为原始对象添加新功能,比继承更灵活。
1.2 核心优势
- 动态添加功能:运行时修改对象行为
- 避免类爆炸:替代多层次的继承结构
- 遵循开闭原则:扩展开放,修改关闭
1.3 典型应用场景
- 需要动态/透明地给对象添加职责
- 需要撤销已添加的功能
- 不适合使用子类扩展的情况
- 常见应用:
- GUI组件装饰
- 流处理(如Java I/O)
- 游戏角色装备系统
- 咖啡订单系统(本文示例)
二、模式结构与C++实现
2.1 UML类图解析
2.2 咖啡订单系统实现
基础组件接口
#include <iostream>
#include <memory>
#include <string>
// 抽象组件
class Beverage {
public:
virtual ~Beverage() = default;
virtual std::string getDescription() const = 0;
virtual double cost() const = 0;
};
// 具体组件:浓缩咖啡
class Espresso : public Beverage {
public:
std::string getDescription() const override {
return "Espresso";
}
double cost() const override {
return 1.99;
}
};
装饰器基类
#include "component.h"
// 抽象装饰器
class CondimentDecorator : public Beverage {
public:
explicit CondimentDecorator(std::unique_ptr<Beverage> beverage) : beverage_(std::move(beverage)) {}
std::string getDescription() const override {
return beverage_->getDescription();
}
double cost() const override {
return beverage_->cost();
}
protected:
std::unique_ptr<Beverage> beverage_;
};
具体装饰器实现
#include "decorator_abstract.h"
// 牛奶装饰器
class Milk : public CondimentDecorator {
public:
using CondimentDecorator::CondimentDecorator;
std::string getDescription() const override {
return beverage_->getDescription() + ", Milk";
}
double cost() const override {
return beverage_->cost() + 0.45;
}
};
// 摩卡装饰器
class Mocha : public CondimentDecorator {
public:
using CondimentDecorator::CondimentDecorator;
std::string getDescription() const override {
return beverage_->getDescription() + ", Mocha";
}
double cost() const override {
return beverage_->cost() + 0.60;
}
};
三、客户端使用示例
3.1 基础使用
#include <iostream>
#include <memory>
#include <string>
#include "decorator_entity.h"
int main() {
// 创建基础咖啡
std::unique_ptr<Beverage> coffee = std::make_unique<Espresso>();
// 第一次装饰:加牛奶
coffee = std::make_unique<Milk>(std::move(coffee));
// 第二次装饰:加摩卡
coffee = std::make_unique<Mocha>(std::move(coffee));
// 输出结果
std::cout << "Order: " << coffee->getDescription() << "\n"
<< "Total Cost: $" << coffee->cost() << std::endl;
return 0;
}
3.2 输出结果
Order: Espresso, Milk, Mocha
Total Cost: $3.04
四、模式深度解析
4.1 设计亮点
- 动态组合:通过嵌套装饰实现功能叠加
- 透明性:装饰器与组件接口一致
- 弹性扩展:新装饰器不影响现有代码
- 智能指针管理:自动内存管理
4.2 与传统继承对比
特性 | 继承方案 | 装饰者模式 |
---|---|---|
扩展方式 | 编译时静态扩展 | 运行时动态组合 |
类数量 | 指数级增长(组合爆炸) | 线性增长 |
功能叠加 | 固定组合 | 任意顺序组合 |
维护成本 | 修改基类影响所有子类 | 独立扩展互不影响 |
4.3 实现注意事项
- 接口一致性:装饰器必须完全实现组件接口
- 装饰顺序:不同装饰顺序可能影响最终结果
- 内存管理:
- 使用
unique_ptr
自动管理生命周期 - 禁止拷贝操作(示例中已隐式实现)
- 使用
- 性能考量:多层嵌套可能影响性能
五、进阶实现技巧
5.1 装饰器撤销功能
// 移除装饰器类
template <typename T>
class RemovableDecorator : public CondimentDecorator {
public:
using CondimentDecorator::CondimentDecorator;
// 移除特定类型的装饰
std::unique_ptr<Beverage> removeDecorator() {
if (auto dec = dynamic_cast<T*>(beverage_.get())) {
return std::move(dec->beverage_);
}
return std::move(beverage_);
}
std::string getDescription() const override {
return beverage_->getDescription();
}
double cost() const override {
return beverage_->cost();
}
};
5.2 复合装饰器
// 复合装饰器 - 两倍装饰 将所选饮料配料翻倍
class DoubleDecorator : public CondimentDecorator {
public:
using CondimentDecorator::CondimentDecorator;
std::string getDescription() const override {
return "'" + beverage_->getDescription() + "' x2";
}
double cost() const override {
return beverage_->cost() * 2;
}
};
5.3 调用方法
#include <iostream>
#include <memory>
#include <string>
#include "decorator_entity.h"
int main() {
// 创建基础咖啡
std::unique_ptr<Beverage> coffee = std::make_unique<Espresso>();
// 第一次装饰:加牛奶
coffee = std::make_unique<Milk>(std::move(coffee));
// 第二次装饰:加摩卡
coffee = std::make_unique<Mocha>(std::move(coffee));
// 输出结果
std::cout << "Order: " << coffee->getDescription() << "\n"
<< "Total Cost: $" << coffee->cost() << std::endl;
// 使用示例
auto rmver = std::make_unique<RemovableDecorator<Mocha>>(std::move(coffee));
coffee = rmver->removeDecorator();
// 输出结果
std::cout << "Order: " << coffee->getDescription() << "\n"
<< "Total Cost after remove Mocha: $" << coffee->cost() << std::endl;
coffee = std::make_unique<DoubleDecorator>(std::move(coffee));
std::cout << "Order: " << coffee->getDescription() << "\n"
<< "Total Cost after double order: $" << coffee->cost() << std::endl;
return 0;
}
5.4 执行结果
Order: Espresso, Milk, Mocha // 初始点单
Total Cost: $3.04
Order: Espresso, Milk // 移除Mocha
Total Cost after remove Mocha: $2.44
Order: 'Espresso, Milk' x2 // 复合装饰器,double order
Total Cost after double order: $4.88
六、模式应用扩展
6.1 实际工程案例
- 图形界面组件:
- 滚动条装饰文本框
- 边框装饰图片组件
- 网络通信:
- 加密装饰数据流
- 压缩装饰传输流
- 游戏开发:
- 装备系统增强角色属性
- 技能BUFF叠加效果
6.2 相关模式对比
模式 | 核心区别 |
---|---|
适配器模式 | 改变对象接口 |
代理模式 | 控制访问,不添加功能 |
组合模式 | 统一处理对象集合 |
策略模式 | 整体替换算法 |
七、最佳实践指南
7.1 适用场景判断
✅ 推荐使用:
- 需要动态添加/撤销功能
- 使用继承会导致类爆炸
- 不同功能的排列组合场景
❌ 避免使用:
- 需要直接访问具体组件方法
- 装饰顺序影响业务逻辑
- 性能敏感的底层系统
7.2 实现原则
- 优先组合:使用对象组合而非继承
- 保持轻量:装饰器不应包含复杂逻辑
- 接口隔离:定义明确的组件接口
- 文档规范:明确装饰器的叠加规则
八、总结
装饰者模式通过灵活的对象组合代替僵化的类继承,为系统扩展提供了优雅的解决方案。在C++实现中:
- 使用
unique_ptr
管理组件生命周期 - 通过抽象类保证接口一致性
- 利用现代C++特性简化实现
当面对需要动态扩展功能的场景时,装饰者模式就像编程世界的"俄罗斯套娃",让每个装饰器层层包裹核心组件,最终组合出强大的功能集合。