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

【读书笔记】《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.lryc.cn/news/586628.html

相关文章:

  • 设计模式:软件开发的高效解决方案(单例、工厂、适配器、代理)
  • 基于无人机 RTK 和 yolov8 的目标定位算法
  • 一文认识并学会c++模板(初阶)
  • AI 助力编程:Cursor Vibe Coding 场景实战演示
  • 基于 Redisson 实现分布式系统下的接口限流
  • 牛客网50题
  • 【C/C++】编译期计算能力概述
  • [Python] -实用技巧篇1-用一行Python代码搞定日常任务
  • python-range函数
  • 校园幸运抽(抽奖系统)测试报告
  • 第七章应用题
  • HT8313功放入门
  • HashMap的原理
  • 数据结构与算法之美:线索二叉树
  • 蒙特卡洛树搜索方法实践
  • 蓝牙调试抓包工具--nRF Connect移动端 使用详细总结
  • 生成式对抗网络(GAN)模型原理概述
  • Java生产带文字、带边框的二维码
  • 牛客:HJ19 简单错误记录[华为机考][字符串]
  • 009 ST表:静态区间最值的极致优化
  • 面试现场:奇哥扮猪吃老虎,RocketMQ高级原理吊打面试官
  • MyBatis实现分页查询-苍穹外卖笔记
  • comfyUI-controlNet-线稿软边缘
  • python-enumrate函数
  • HarmonyOS从入门到精通:动画设计与实现之六 - 动画曲线与运动节奏控制
  • houdini 用 vellum 制作一个最简单的布料
  • 洛谷题解 | UVA1485 Permutation Counting
  • C++结构体数组应用
  • Spring Boot 中使用 Lombok 进行依赖注入的示例
  • 基于springboot+Vue的二手物品交易的设计与实现(免费分享)