当前位置: 首页 > 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.dtcms.com/a/277910.html

相关文章:

  • HTML 基本骨架
  • [GWCTF 2019]我有一个数据库
  • SOMEIP协议与测试
  • LeetCode 2401.最长优雅子数组
  • C++数组指针与函数指针
  • 为什么要有延时回调?
  • 2024-2025-2 山东大学《软件工程与实践》期末(回忆版)
  • p4 大小写检查
  • C++高级编程,类模版成员函数类外实现
  • windows10如何安装vue开发环境
  • JAVA-springboot 整合Activemq
  • ECU(电子控制单元)是什么?
  • C++中顶层const与底层const
  • JSX 语法
  • 【前端知识】移动端APP原生应用与H5交互底层逻辑
  • Dubbo跨越分布式事务的最终一致性陷阱
  • 有效感受野(ERF)可视化工具
  • hash表的模拟--开放定址法
  • 如何将本地代码同步到远程Github仓库
  • 【Docker基础】Dockerfile指令速览:环境与元数据指令详解
  • OSPF与BGP的联动特性
  • Utils系列之内存池(MultiSizePool)
  • 【MLLM】多模态理解GLM-4.1V-Thinking模型
  • OpenVela 日志系统:从配置到落地的实操手册
  • Python装饰器(自定义装饰器和3个内置装饰器)
  • Java反射机制深度解析
  • 树莓派5-ollama-linux-arm64.tgz 下载
  • AEC线性处理
  • 在 OCI 生成式 AI 上搭一个「指定地区拉面店 MCP Server」——从 0 到 1 实战记录
  • 《数据库》MySQL事务