【C++设计模式】第九篇:装饰器模式(Decorator)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

动态扩展对象功能的利器


1. 模式定义与用途​

核心思想

  • 装饰器模式:通过组合对象而非继承,动态地给对象添加额外职责。
  • 关键用途
    1.运行时扩展功能:无需修改原有类,支持按需叠加功能(如加密、压缩)。
    2.替代多层继承:避免因功能组合导致的子类爆炸问题。

经典场景

  • 数据流处理:为网络传输动态添加加密、压缩、缓存等能力。
  • 日志系统:为日志消息添加时间戳、来源IP等元信息。

2. 模式结构解析

UML类图

plaintext 复制代码
+---------------------+          +---------------------+  
|       Component     |          |       Decorator      |  
+---------------------+          +---------------------+  
| + operation(): void |<|---------| - component: Component  
+---------------------+          | + operation(): void |  
          ^                      +---------------------+  
          |                                ^  
          |                        +-------+------------+  
          |                        |                    |  
+---------------------+    +--------------------+  +-------------------+  
|  ConcreteComponent  |    | ConcreteDecoratorA | | ConcreteDecoratorB |  
+---------------------+    +--------------------+ +--------------------+  
| + operation()       |    | + operation()      | | + operation()      |  
+---------------------+    +--------------------+ +--------------------+  

角色说明

  1. Component:抽象接口,定义核心功能方法(如send())。
  2. ​ConcreteComponent:具体组件,实现基础功能(如明文数据发送)。
  3. Decorator:装饰器基类,持有组件对象并转发请求。
  4. ConcreteDecorator:具体装饰器,添加扩展功能(如加密)。

3. 现代C++实现示例

场景:网络数据流动态装饰

​步骤1:定义抽象数据流接口
cpp 复制代码
class DataStream {  
public:  
    virtual ~DataStream() = default;  
    virtual void send(const std::string& data) = 0;  
};  
步骤2:实现基础数据流(明文传输)
cpp 复制代码
class PlainStream : public DataStream {  
public:  
    void send(const std::string& data) override {  
        std::cout << "发送明文: " << data << "\n";  
    }  
};  
步骤3:定义装饰器基类
cpp 复制代码
class StreamDecorator : public DataStream {  
public:  
    StreamDecorator(std::unique_ptr<DataStream> stream)  
        : stream_(std::move(stream)) {}  

    void send(const std::string& data) override {  
        stream_->send(data);  // 默认转发请求  
    }  

protected:  
    std::unique_ptr<DataStream> stream_;  
};  
步骤4:实现具体装饰器(加密与压缩)​
cpp 复制代码
// 加密装饰器  
class EncryptedStream : public StreamDecorator {  
public:  
    using StreamDecorator::StreamDecorator;  

    void send(const std::string& data) override {  
        auto encrypted = "加密[" + data + "]";  
        stream_->send(encrypted);  
    }  
};  

// 压缩装饰器  
class CompressedStream : public StreamDecorator {  
public:  
    using StreamDecorator::StreamDecorator;  

    void send(const std::string& data) override {  
        auto compressed = "压缩[" + data + "]";  
        stream_->send(compressed);  
    }  
};  
步骤5:客户端动态组合装饰器
cpp 复制代码
int main() {  
    // 基础数据流  
    auto plain = std::make_unique<PlainStream>();  

    // 动态添加装饰功能  
    auto encrypted = std::make_unique<EncryptedStream>(std::move(plain));  
    auto compressedAndEncrypted = std::make_unique<CompressedStream>(std::move(encrypted));  

    compressedAndEncrypted->send("Hello World");  
    // 输出:  
    // 发送明文: 压缩[加密[Hello World]]  
}  

4. 应用场景示例

场景1:日志消息装饰

cpp 复制代码
class Logger {  
public:  
    virtual ~Logger() = default;  
    virtual void log(const std::string& msg) = 0;  
};  

// 基础日志  
class FileLogger : public Logger {  
    void log(const std::string& msg) override { /* 写入文件 */ }  
};  

// 时间戳装饰器  
class TimestampLogger : public Logger {  
public:  
    TimestampLogger(std::unique_ptr<Logger> logger) : logger_(std::move(logger)) {}  

    void log(const std::string& msg) override {  
        auto timestamp = "[2023-10-01 12:00:00] ";  
        logger_->log(timestamp + msg);  
    }  

private:  
    std::unique_ptr<Logger> logger_;  
};  

场景2:图形界面控件装饰

cpp 复制代码
class Button {  
public:  
    virtual void render() = 0;  
};  

// 基础按钮  
class BasicButton : public Button {  
    void render() override { /* 绘制按钮 */ }  
};  

// 边框装饰器  
class BorderDecorator : public Button {  
public:  
    BorderDecorator(std::unique_ptr<Button> button) : button_(std::move(button)) {}  

    void render() override {  
        button_->render();  
        std::cout << "添加红色边框\n";  
    }  

private:  
    std::unique_ptr<Button> button_;  
};  

5. 优缺点分析

优点 ​​缺点
动态扩展对象功能,无需修改源码 装饰器嵌套过多会降低可读性
避免继承层次过深 装饰顺序可能影响结果(如先加密后压缩)
支持运行时灵活组合功能 对象标识改变,typeid检测可能失效

6. 调试与优化策略

调试技巧(VS2022)​

1. 跟踪装饰链调用:
  • 在每个装饰器的send()方法内设置断点,观察数据流处理顺序。
2. 验证装饰器组合:
  • 使用dynamic_cast检查装饰器类型(需启用RTTI)。

性能优化

1. 减少内存分配:
  • 复用装饰器对象(如线程安全的对象池)。
2.批量处理优化:
cpp 复制代码
class BufferedStream : public StreamDecorator {  
public:  
    void send(const std::string& data) override {  
        buffer_.push_back(data);  
        if (buffer_.size() >= 1024) {  
            flush();  
        }  
    }  
private:  
    std::vector<std::string> buffer_;  
    void flush() {  
        stream_->send(concat(buffer_));  
        buffer_.clear();  
    }  
};  
相关推荐
总斯霖28 分钟前
题解:士兵排列
数据结构·c++·算法
稳兽龙33 分钟前
P4268 [USACO18FEB] Directory Traversal G
c++·算法·换根dp
放氮气的蜗牛1 小时前
C++从入门到精通系列教程之第十篇:异常处理与调试技巧
开发语言·jvm·c++
LL5962145691 小时前
CEF在MFC上的示例工程
c++·mfc·cef
caoruipeng2 小时前
Windows编程----进程的当前目录
c++·windows·c#
誓约酱2 小时前
(每日一题) 力扣 283 移动零
linux·c语言·数据结构·c++·算法·leetcode
老狼主2 小时前
C++ Windows下屏幕截图
c++·windows
福鸦3 小时前
C++ STL深度解析:现代编程的瑞士军刀
开发语言·c++·算法·安全·架构
DemonAvenger3 小时前
深入Go并发编程:Goroutine性能调优与实战技巧全解析
设计模式·架构·go
橘子真甜~3 小时前
34.二叉树进阶3(平衡二叉搜索树 - AVL树及其旋转操作图解)
数据结构·c++·二叉搜索树·avl树·平衡搜索树