当前位置: 首页 > java >正文

【读书笔记】《C++ Software Design》第九章:The Decorator Design Pattern

《C++ Software Design》第九章:The Decorator Design Pattern

在现实系统中,我们常常需要在不修改已有类的前提下,为对象添加新功能——比如增加日志记录、性能度量、缓存行为等等。这种按需叠加式功能扩展是软件工程中典型的需求。

Decorator(装饰器)模式正是为此而生的一种经典结构型设计模式。它提供了一种透明、组合式的方式来增强对象的行为,是继承替代方案中最灵活的一种。


Guideline 35:使用装饰器层次化地扩展功能

可能遇到的问题

假设你负责一个 DataSource 类,有人需要在读取数据时加密,另一个同事希望加上缓存,还有人需要日志跟踪。传统继承方式无法灵活组合这些需求:

class EncryptedDataSource : public DataSource {};
class CachedDataSource : public DataSource {};
class LoggingDataSource : public DataSource {};

那如果有人想要“加密+缓存+日志”呢?就不得不写出一系列组合子类:EncryptedCachedDataSourceWithLogging ……代码膨胀不可避免。


Decorator 模式解释

Decorator 模式通过组合而不是继承,将增强行为附加到已有对象上,并允许以层级结构形式叠加功能。

核心结构:
class DataSource {
public:virtual std::string read() = 0;virtual ~DataSource() = default;
};class FileDataSource : public DataSource {
public:std::string read() override {return "file_data";}
};class DataSourceDecorator : public DataSource {
protected:std::shared_ptr<DataSource> wrappee;
public:DataSourceDecorator(std::shared_ptr<DataSource> src) : wrappee(std::move(src)) {}
};class LoggingDecorator : public DataSourceDecorator {
public:using DataSourceDecorator::DataSourceDecorator;std::string read() override {std::cout << "[LOG] Reading data\n";return wrappee->read();}
};class CachingDecorator : public DataSourceDecorator {std::string cachedData;
public:using DataSourceDecorator::DataSourceDecorator;std::string read() override {if (cachedData.empty()) {cachedData = wrappee->read();}return cachedData;}
};

使用方式:

auto src = std::make_shared<FileDataSource>();
auto cached = std::make_shared<CachingDecorator>(src);
auto logged = std::make_shared<LoggingDecorator>(cached);std::string data = logged->read();

你可以任意组合装饰器而不改变底层类。


第二个例子:装饰图形对象

class Shape {
public:virtual void draw() const = 0;
};class Circle : public Shape {
public:void draw() const override {std::cout << "Drawing Circle\n";}
};class ColorDecorator : public Shape {std::shared_ptr<Shape> shape;std::string color;
public:ColorDecorator(std::shared_ptr<Shape> s, std::string c): shape(std::move(s)), color(std::move(c)) {}void draw() const override {shape->draw();std::cout << "With color: " << color << "\n";}
};

Decorator vs Adapter vs Strategy

模式作用是否替代接口是否组合行为使用粒度
Decorator添加可选功能实例
Adapter转换接口类/接口
Strategy替换算法

Decorator 最大的特点是“功能增强叠加而非行为切换”。


Decorator 的短板分析

  • 对于每个功能点都需一个子类,复杂度线性增长
  • 不适合替换核心行为,只适合“附加行为”
  • 调试时可能形成长链,调试栈难以定位
  • 默认实现为运行时结构,可能存在虚函数调用开销

Guideline 36:理解运行时与编译时抽象的权衡

C++ 作为静态类型语言,其类型系统可在编译期执行许多抽象操作。Decorator 模式既可在运行时组合(如上所示),也可借助模板系统在编译期静态组合,从而实现零开销的增强。


编译时 Decorator:值语义的组合

通过模板类嵌套实现组合行为:

template <typename Base>
class LoggingDecorator {
public:Base base;void run() {std::cout << "[LOG] Before\n";base.run();std::cout << "[LOG] After\n";}
};class Task {
public:void run() {std::cout << "Running task\n";}
};

使用:

LoggingDecorator<Task> task;
task.run();

特点:

  • 零运行时开销(无虚函数)
  • 组合发生在编译期
  • 更易内联优化

运行时 Decorator:基于值语义封装

可用 std::function 或类型擦除实现值类型的 runtime decorator:

std::function<void()> f = [] {std::cout << "Real task\n";
};std::function<void()> logWrapper = [f]() {std::cout << "[LOG] Start\n";f();std::cout << "[LOG] End\n";
};logWrapper();

适用于任务调度、管线执行等异步场景。


对比分析

特性编译时 Decorator运行时 Decorator
性能高(无虚调用)中等(虚调用或类型擦除)
灵活性差(必须编译期组合)高(运行时可决定组合)
可维护性中(模板膨胀)高(抽象清晰)
类型侵入性强(需暴露模板类型)弱(可封装任意对象)

小结

Decorator 模式在 C++ 中拥有灵活实现方式:

  • 作为运行时功能扩展机制,它是继承结构的有效替代
  • 借助模板,可在编译期静态组合增强,获得更优性能
  • 适合场景包括日志增强、缓存包装、动态功能组合、绘图增强等

搭配第八章的类型擦除技术,Decorator 模式还可以实现 非侵入式、运行时组合的功能链条,在现代微服务、中间件框架、事件驱动系统中发挥重要作用。

在高质量 C++ 软件架构设计中,Decorator 模式帮助我们延迟功能绑定、支持渐进式增强,避免陷入硬编码的继承层级。同时,它也体现了“组合优于继承”的软件设计哲学。

http://www.xdnf.cn/news/15323.html

相关文章:

  • LeetCode 1156.单字符重复子串的最大长度
  • 代码部落 20250713 CSP-J复赛 模拟赛
  • 婚后才明白,原来结婚真需要一点冲动!
  • 时序预测 | Matlab代码实现VMD-TCN-GRU-MATT变分模态分解时间卷积门控循环单元多头注意力多变量时序预测
  • (一)SAP Group Reporting (GR) 集团财务合并解决方案套件概述
  • java 基本数据类型所对应的包装类
  • 暑期自学嵌入式——Day01(C语言阶段)
  • C++中顶层const与底层const
  • 【开源项目】网络诊断告别命令行!NetSonar:开源多协议网络诊断利器
  • 【研报复现】开源证券:均线的收敛与发散
  • 从 Manifest V2 升级到 Manifest V3:常见问题与解决方案
  • exe文件图标修改器 - exe图标提取器(ico、png) - 修改360文件夹的图标为windows自带的图标
  • # 通过wifi共享打印机只有手动翻页正反打印没有自动翻页正反打印,而通过网线连接的主机电脑可以自动翻页正反打印
  • 设计模式:软件开发的高效解决方案(单例、工厂、适配器、代理)
  • 预处理器完整功能介绍和示例演示(LESS/SCSS)
  • DMDIS文件到数据库
  • 并查集 UnionFind Test01
  • 什么是RAG(Retrieval-Augmented Generation)?一文读懂检索增强生成
  • websocket连接时发生未知错误
  • SAP顾问职位汇总(第28周)
  • 快速生成 Android 的 Splash 的 9 Patch 图片
  • phpMyAdmin:一款经典的MySQL在线管理工具又回来了
  • DNS解析过程和nmap端口扫描
  • 【STM32实践篇】:F407 时钟系统
  • MacOS使用Multipass快速搭建轻量级k3s集群
  • Spring Boot 安全登录系统:前后端分离实现
  • ERA5的UV合并成矢量并按时间维度转为nc或tif
  • 【版本控制】Perforce Helix Core (P4V) 完全入门指南(含虚幻引擎实战)
  • Spring Boot 集成 Spring Security 完整示例
  • C++ 单例模式实现