C++设计模式(GOF-23)——04 C++装饰器模式(Decorator)(一个类同时继承和组合另一个类)解决类爆炸问题、模板装饰器
文章目录
- 1. 装饰器模式的核心思想
- 2. 装饰器模式的结构
- 关键点:
- 类图关系说明
- 3. 装饰器模式的适用场景
- 示例:流操作功能扩展
- 动态组合调用流程
- 调用流程图示
- 4. 装饰器模式与组合模式的区别
- 5. 总结
- 装饰器模式关键点
- 代码优化建议
在 C++ 中,当一个类 同时继承和组合另一个类时,这种设计模式通常被称为 装饰器模式(Decorator Pattern)。以下是详细解释:
1. 装饰器模式的核心思想
装饰器模式是一种结构型设计模式,其核心目的是通过组合和继承的方式,动态地为对象添加职责,而不通过继承硬编码的方式来扩展功能。
- 继承:装饰器类(Decorator)继承自目标类(Component)的接口或抽象类。
- 组合:装饰器类内部组合一个目标类的对象(通常是通过构造函数传入),从而在运行时动态地扩展功能。
这种设计模式允许你在不修改原有类代码的情况下,通过组合的方式灵活地扩展功能,同时避免了继承可能导致的类爆炸问题。
2. 装饰器模式的结构
装饰器模式的典型结构如下:
// 抽象组件接口,定义核心操作
class Component {
public:virtual void Operation() = 0; // 纯虚函数,强制子类实现virtual ~Component() {} // 虚析构函数确保安全释放资源
};// 具体组件类,实现核心功能
class ConcreteComponent : public Component {
public:void Operation() override {std::cout << "ConcreteComponent Operation" << std::endl;}
};// 抽象装饰器类,继承自组件接口并组合组件对象
class Decorator : public Component {
protected:Component* component_; // 持有组件对象指针
public:// 通过构造函数注入组件对象Decorator(Component* component) : component_(component) {}void Operation() override {component_->Operation(); // 默认调用被装饰对象的操作}
};// 具体装饰器A,扩展新功能
class ConcreteDecoratorA : public Decorator {
private:std::string addedState_; // 新增状态字段
public:// 通过构造函数注入组件对象ConcreteDecoratorA(Component* component) : Decorator(component) {}void Operation() override {// 调用父类(装饰器基类)的OperationDecorator::Operation();// 添加新功能std::cout << "ConcreteDecoratorA added functionality" << std::endl;}
};// 具体装饰器B,扩展新功能
class ConcreteDecoratorB : public Decorator {
private:std::string addedBehavior_; // 新增行为字段
public:ConcreteDecoratorB(Component* component) : Decorator(component) {}void Operation() override {// 调用父类(装饰器基类)的OperationDecorator::Operation();// 添加新功能std::cout << "ConcreteDecoratorB added functionality" << std::endl;}
};
关键点:
- 继承:
Decorator
类继承自Component
,从而能够替代Component
的使用。 - 组合:
Decorator
类内部组合一个Component
对象(通过构造函数传入),并在其方法中调用该对象的方法。 - 动态扩展:通过嵌套多个装饰器(如
ConcreteDecoratorA
和ConcreteDecoratorB
),可以在运行时动态地添加功能。
类图关系说明
3. 装饰器模式的适用场景
- 需要动态、透明地给对象添加职责,而不是通过继承硬编码。
- 扩展功能时,不希望使用静态继承导致的类爆炸。
- 需要为对象添加多个可选的功能,并且这些功能可以独立组合。
示例:流操作功能扩展
假设有一个 Stream
接口,需要为不同的流(如文件流、网络流)添加压缩、加密等功能:
// 流接口定义
class Stream {
public:virtual void Write(const std::string& data) = 0; // 核心操作virtual ~Stream() {} // 虚析构函数
};// 具体文件流实现
class FileStream : public Stream {
public:void Write(const std::string& data) override {std::cout << "Writing to file: " << data << std::endl;}
};// 压缩装饰器,组合流对象
class CompressedStream : public Stream {
private:Stream* stream_; // 持有流对象指针
public:// 通过构造函数注入流对象CompressedStream(Stream* stream) : stream_(stream) {}void Write(const std::string& data) override {// 数据压缩逻辑std::string compressedData = "Compressed(" + data + ")";// 调用被装饰对象的Write方法stream_->Write(compressedData);}
};// 加密装饰器,组合流对象
class EncryptedStream : public Stream {
private:Stream* stream_; // 持有流对象指针
public:// 通过构造函数注入流对象EncryptedStream(Stream* stream) : stream_(stream) {}void Write(const std::string& data) override {// 数据加密逻辑std::string encryptedData = "Encrypted(" + data + ")";// 调用被装饰对象的Write方法stream_->Write(encryptedData);}
};
动态组合调用流程
int main() {// 创建基础对象Stream* fileStream = new FileStream();// 添加压缩功能Stream* compressedStream = new CompressedStream(fileStream);// 添加加密功能Stream* encryptedStream = new EncryptedStream(compressedStream);// 执行写入操作encryptedStream->Write("Hello, World!");// 释放资源(建议使用智能指针)delete encryptedStream;return 0;
}
调用流程图示
输出结果:
Writing to file: Compressed(Encrypted(Hello, World!))
4. 装饰器模式与组合模式的区别
特性 | 装饰器模式 | 组合模式 |
---|---|---|
核心目的 | 动态扩展对象功能 | 构建树形结构 |
对象关系 | 1:1 的装饰关系 | 1:N 的父子关系 |
调用方式 | 链式调用 | 递归调用 |
典型应用场景 | GUI控件装饰、IO流增强 | 文件系统、组织架构 |
(装饰器继承组件,又包含组件)
5. 总结
装饰器模式关键点
特性 | 描述 |
---|---|
设计模式名称 | 装饰器模式(Decorator Pattern) |
核心思想 | 通过继承和组合动态扩展对象功能 |
优点 | - 避免继承的类爆炸 - 动态、灵活地添加功能 - 符合开闭原则 |
适用场景 | 需要动态扩展对象功能,且功能可以独立组合 |
代码优化建议
- 使用
std::unique_ptr
或std::shared_ptr
替代原始指针,避免内存泄漏 - 对于复杂装饰链,考虑使用工厂模式创建装饰器组合
- 在C++11/14中可使用模板装饰器实现类型安全的装饰逻辑
// 示例:模板装饰器
template <typename T>
class DecoratorTemplate : public T {
protected:T* component_;
public:DecoratorTemplate(T* component) : component_(component) {}void Operation() override {component_->Operation();}
};