用装饰器模式扩展DataProcessor:所有装饰器实现统一Writer接口,通过std::unique_ptr组合串联,不继承、不修改原类;依赖注入接管生命周期,头文件隔离实现细节。

用装饰器模式包装核心类,不碰原有 DataProcessor 的一行代码
新增功能(如日志、压缩、加密)直接写成独立的装饰器类,每个只关注单一职责。它们都继承自统一的抽象接口(比如 Writer),内部持有一个 std::unique_ptr<writer></writer> 指向被装饰对象。调用时,装饰器先执行自己的逻辑(如记录日志),再转发给下游——旧类完全无感知,也不需要加虚函数或改访问权限。
常见错误是把装饰器写成“继承+重写”,结果又掉进子类爆炸陷阱;正确做法是让所有装饰器和原始类实现同一接口,靠组合串联,而非靠派生树分支。
- 原始类保持 final(可选),明确禁止继承
- 所有装饰器构造函数接收
std::unique_ptr<writer></writer>,强制依赖抽象,不绑定具体类型 - 避免在装饰器里暴露被装饰对象的内部细节(如不提供
get_raw()方法),否则会破坏封装边界
把变化点抽成策略参数,用 std::function 或模板传入
如果新功能只是算法替换(比如校验从 CRC16 换成 SHA256),别改类的成员函数,而是把校验逻辑抽象为可调用对象。在 DataProcessor 构造或配置阶段传入 std::function<bool std::string></bool>,内部只负责调用它。
相比虚函数多态,std::function 更轻量,且支持 lambda、函数指针、绑定对象等多种形式;模板方案(如 template<typename validator></typename>)则零开销,但会增加实例化数量。
立即学习“C++免费学习笔记(深入)”;
- 运行时切换用
std::function,编译期确定用模板 - 注意
std::function的小对象优化限制:捕获太多状态可能触发堆分配 - 不要在接口中暴露策略类型的模板参数,否则使用者必须显式指定,破坏易用性
通过依赖注入接管对象生命周期,避免硬编码 new
旧类里如果写了 new FileLogger() 这种代码,新增功能就必须改它。正确做法是让外部创建好所需对象(比如 BufferedWriter 套着 EncryptedWriter 套着 FileWriter),再通过构造函数或 setter 注入到 DataProcessor 中。
这要求你提前把对象创建逻辑移出业务类——不是“类自己造自己需要的东西”,而是“别人把东西准备好交给我”。这样,加一个缓存装饰器,只需改创建链,不碰 DataProcessor 本身。
- 优先用构造函数注入,语义清晰且不可变
- 避免 setter 注入后被多次调用导致状态混乱
- 若使用工厂,工厂返回的应是接口指针(
std::unique_ptr<writer></writer>),而非具体类型
警惕头文件泄露和隐式依赖,否则“没改类”也等于白搭
即使你没动 DataProcessor.cpp,只要它的头文件 DataProcessor.h 里 #include "old_feature.h",而这个头文件又被新功能修改过,那么所有包含 DataProcessor.h 的地方都会被迫重编译——这本质上仍是“对旧类的影响”。
真正隔离变更,得从头文件开始:把实现细节移到 .cpp 文件里,用前置声明 + pimpl 惯用法隐藏私有成员类型;对外只暴露纯接口头文件(如 writer_interface.h),不暴露任何具体装饰器的定义。
- 检查
DataProcessor.h的 include 列表,删掉所有非必需头文件 - 把
std::unique_ptr<concretedecorator></concretedecorator>改成std::unique_ptr<writer></writer>,前置声明class Writer;即可 - 若用了 pimpl,确保
std::unique_ptr<impl></impl>在 .cpp 里定义,头文件中仅声明


















