# C++设计模式:装饰器模式——动态扩展功能的优雅方案 在软件开发中,我们经常需要给现有对象添加新功能。传统的方法存在诸多问题: **场景 1:继承的局限性** 假设你有一个文本处理类,需要添加多种功能(加密、压缩、缓存等): ```cpp // 基础类 class TextProcessor { public: virtual std::string process(const std::string& text) { return text; } }; // 使用继承添加功能 class EncryptedTextProcessor : public TextProcessor { // 添加加密功能 }; classCompressedTextProcessor : public TextProcessor { // 添加压缩功能 }; // 问题:如果需要同时加密和压缩怎么办? // 需要创建 EncryptedCompressedTextProcessor // 如果还有缓存功能?需要 EncryptedCompressedCachedTextProcessor // 组合爆炸!类数量呈指数增长 ``` **场景 2:功能组合的复杂性** 如果需要动态组合功能,继承方式完全无法满足: ```cpp // 需求:运行时决定是否加密、是否压缩、是否缓存 // 继承方式:需要为每种组合创建一个类(2^3 = 8 个类) // 如果功能增加到 5 个?需要 2^5 = 32 个类! ``` **场景 3:违反开闭原则** 每次添加新功能,都需要修改现有代码或创建新的子类: ```cpp // 添加新功能"日志记录" // 需要修改所有现有的处理器类 // 或者创建更多组合类 ``` **装饰器模式提供了优雅的解决方案**:通过组合而非继承,动态地给对象添加功能,保持接口一致,避免类爆炸。 ### 装饰器模式的核心思想 装饰器模式就像生活中的**包装盒**: - **原始对象**:一个礼物 - **装饰器**:各种包装纸、丝带、卡片 - **最终效果**:可以任意组合包装,每次包装都保持"礼物"的本质 ```cpp [礼物] → [包装纸] → [丝带] → [卡片] → 最终礼物 原始 装饰1 装饰2 装饰3 层层包装 ``` 装饰器的关键特点: - **透明性**:装饰后的对象可以替代原始对象 - **可组合性**:可以任意组合多个装饰器 - **动态性**:运行时决定使用哪些装饰器 ## 装饰器模式详解 ### 装饰器模式的本质与设计哲学 装饰器模式解决的核心问题是**功能扩展**,它体现了几个重要的设计思想: **1. 组合优于继承** 装饰器模式通过**组合**而非继承来扩展功能,避免了继承带来的问题: - **避免类爆炸**:不需要为每种组合创建新类 - **灵活组合**:运行时动态组合功能 - **松耦合**:装饰器与被装饰对象之间是组合关系 **2. 开闭原则的完美体现** 装饰器模式完美体现了开闭原则: - **对扩展开放**:可以添加新的装饰器类,无需修改现有代码 - **对修改封闭**:被装饰的类和现有装饰器都不需要修改 **3. 单一职责原则** 每个装饰器只负责一个功能增强: - **加密装饰器**:只负责加密 - **压缩装饰器**:只负责压缩 - **缓存装饰器**:只负责缓存 职责分离使得代码清晰、易于维护。 **4. 透明装饰** 装饰器保持与被装饰对象相同的接口,使得装饰后的对象可以完全替代原始对象,客户端代码无需修改。 ### 模式定义 > **装饰器模式(Decorator Pattern)**:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。 **别名**:Wrapper(包装器),但注意与适配器模式的别名区分 ### 模式角色 装饰器模式包含以下角色: 1. **组件接口(Component)**:定义对象的接口,可以给这些对象动态添加职责 2. **具体组件(ConcreteComponent)**:定义具体的对象,可以给这个对象添加职责 3. **装饰器抽象类(Decorator)**:维护一个指向组件对象的引用,并定义一个与组件接口一致的接口 4. **具体装饰器(ConcreteDecorator)**:向组件添加职责 **角色间的协作关系**: ```bash 客户端 → Component(接口) ↑ ↑ ConcreteComponent Decorator(装饰器基类) ↑ ConcreteDecorator(具体装饰器) ``` ### 基本实现结构 ```cpp // 1. 组件接口 class Component { public: virtual ~Component() = default; virtual void operation() = 0; }; // 2. 具体组件 class ConcreteComponent : public Component { public: void operation() override { std::cout << "具体组件的操作" << std::endl; } }; // 3. 装饰器抽象类 class Decorator : public Component { protected: std::unique_ptr component_; public: Decorator(std::unique_ptr comp) : component_(std::move(comp)) {} void operation() override { if (component_) { component_->operation(); } } }; // 4. 具体装饰器 class ConcreteDecorator : public Decorator { public: ConcreteDecorator(std::unique_ptr comp) : Decorator(std::move(comp)) {} void operation() override { // 添加装饰功能 addedBehavior(); // 调用被装饰对象 Decorator::operation(); // 可以添加后置处理 addedBehaviorAfter(); } private: void addedBehavior() { std::cout << "装饰器添加的前置行为" << std::endl; } void addedBehaviorAfter() { std::cout << "装饰器添加的后置行为" << std::endl; } }; ``` ### 装饰器模式在软件架构中的位置 装饰器模式在软件架构中扮演着**功能增强层**的角色: **1. IO 流处理** 在 IO 流处理中,装饰器模式用于层层包装流对象: ```bash FileInputStream → BufferedInputStream → DataInputStream 原始流 缓冲装饰器 数据装饰器 ``` **2. GUI 组件系统** 在 GUI 系统中,装饰器用于动态添加视觉效果: ```bash Button → ScrollableButton → BorderedButton → ShadowedButton 基础 滚动装饰器 边框装饰器 阴影装饰器 ``` **3. 中间件和过滤器** 在 Web 框架中,装饰器模式用于实现中间件链: ```bash Request → AuthMiddleware → LoggingMiddleware → CacheMiddleware 请求 认证装饰器 日志装饰器 缓存装饰器 ``` ## 装饰器模式与设计原则 ### SOLID 原则在装饰器模式中的体现 **1. 单一职责原则(SRP)** 每个装饰器只负责一个功能增强: ```cpp // ✅ 好:每个装饰器职责单一 class EncryptedDecorator { /* 只负责加密 */ }; class CompressedDecorator { /* 只负责压缩 */ }; class CachedDecorator { /* 只负责缓存 */ }; // ❌ 差:一个装饰器承担多个职责 class MultiFeatureDecorator { /* 同时负责加密、压缩、缓存 */ }; ``` **2. 开闭原则(OCP)** 装饰器模式完美体现开闭原则: - **对扩展开放**:可以添加新装饰器,无需修改现有代码 - **对修改封闭**:组件和现有装饰器都不需要修改 ```cpp // 添加新功能:日志装饰器 class LoggingDecorator : public Decorator { // 无需修改任何现有代码 }; // 可以立即使用 auto processor = std::make_unique( std::make_unique() ); ``` **3. 里氏替换原则(LSP)** 装饰器可以完全替代被装饰的组件: ```cpp void useComponent(Component* comp) { comp->operation(); // 应该能正常工作 } // 所有这些都是有效的 useComponent(new ConcreteComponent()); useComponent(new DecoratorA(new ConcreteComponent())); useComponent(new DecoratorB(new DecoratorA(new ConcreteComponent()))); ``` **4. 依赖倒置原则(DIP)** 装饰器依赖抽象(Component 接口)而非具体实现: ```cpp class Decorator : public Component { std::unique_ptr component_; // 依赖抽象 // 不依赖 ConcreteComponent }; ``` **5. 接口隔离原则(ISP)** 装饰器只实现需要的接口方法,不添加不必要的方法。 ### 装饰器模式的理论意义 **1. 组合优于继承** 装饰器模式是"组合优于继承"原则的典型应用: - **继承的问题**:类爆炸、编译时确定、紧耦合 - **组合的优势**:灵活组合、运行时确定、松耦合 **2. 职责链模式的基础** 装饰器模式可以看作是职责链模式的一种特殊形式,每个装饰器是链中的一个节点。 **3. 功能正交性** 装饰器模式体现了功能的正交性:每个装饰器的功能是独立的,可以任意组合。 ## 装饰器模式的最佳实践 ### 何时使用装饰器模式 **✅ 适合使用的场景**: - **动态添加功能**:需要在运行时动态组合功能 - **功能可组合**:功能之间相互独立,可以任意组合 - **避免类爆炸**:使用继承会导致类数量爆炸 - **保持接口一致**:需要保持与原始对象相同的接口 **❌ 不适合使用的场景**: - **功能不可组合**:功能之间有依赖关系或冲突 - **简单功能扩展**:如果只需要简单添加一个功能,继承可能更简单 - **性能敏感**:装饰器会引入多层调用,可能影响性能 ## 总结 装饰器模式是功能扩展的优雅解决方案,在实际开发中应用广泛。 **开发经验**: 在一个文本处理系统中,需要支持加密、压缩、缓存、日志等多种功能。如果使用继承,需要创建 2^4 = 16 个类。使用装饰器模式后,只需要 5 个类(1 个组件 + 4 个装饰器),并且可以动态组合,大大简化了代码结构。 **记住这句话**: > **组合优于继承,装饰器模式让功能扩展变得灵活而优雅。** > **通过装饰器,我们可以在不修改原有代码的情况下,动态地给对象添加新功能。**