外观模式的核心是封装子系统接口,需通过组合方式用引用或智能指针管理已有子系统对象,方法命名应体现业务语义而非调用顺序,并采用前向声明或PIMPL避免头文件依赖爆炸。

外观模式的核心是封装子系统接口,不是写个包装类就完事
外观模式(Facade)在 C++ 中本质是提供一个统一、简化的接口去访问一组复杂的子系统类。它不改变子系统行为,也不引入新功能,只做“门面”——关键在于识别哪些耦合该被隔离、哪些接口该被收敛。很多人一上来就写个 Facade 类把所有子系统对象 new 出来塞进构造函数,结果导致内存泄漏、生命周期错乱或头文件爆炸。
用组合而非继承,且优先用引用/指针管理子系统实例
子系统类通常已有独立职责和生命周期,外观类不该接管它们的构造与析构。传入已存在的对象更安全、更灵活。
- 避免在
Facade构造函数里直接new子系统对象;改用构造函数参数接收SubsystemA&或std::unique_ptr<subsystemb></subsystemb> - 若子系统对象可能为空,用
std::optional<:reference_wrapper>></:reference_wrapper>或裸指针(加注释说明非空约定)比默认构造 + 后续赋值更清晰 - 不要让
Facade继承任何子系统类——外观不是子系统的变体,它是协调者
方法命名要反映业务意图,而不是拼接子系统函数名
常见错误是把 facade.doStep1ThenStep2() 直接映射成 a->init(); b->process(); c->finalize();,这等于把调用顺序暴露给了客户端。真正有用的外观应该命名如 startBatchImport() 或 shutDownSafely(),让调用方只关心“做什么”,不关心“怎么做”。
- 每个公开方法应对应一个完整语义单元,比如一次配置加载、一次设备校准流程
- 若某子系统调用需条件判断(如仅当
isDebugMode为真才调用logger->logVerbose()),逻辑必须封装在外观内部,不暴露给上层 - 避免在外观方法里返回子系统原始指针(如
SubsystemA*)——这会绕过门面,破坏封装边界
注意头文件依赖爆炸:用前向声明 + PIMPL 是实用底线
外观类若直接包含所有子系统头文件(#include "SubsystemA.h"、"SubsystemB.h"…),会导致任何包含 Facade.h 的地方都得重编译整个子系统链。这不是设计问题,是构建效率灾难。
- 对只用于指针/引用的子系统类型,在
Facade.h中用前向声明(class SubsystemA;),把#include移到Facade.cpp中 - 若需在外观中持有子系统对象(而非指针),且不想暴露实现细节,用
pimpl惯用法:在头文件中只放struct Impl;和std::unique_ptr<impl> pimpl_;</impl>,所有子系统成员和逻辑移到 .cpp 文件里 - 别为了“整洁”把所有子系统抽象成统一接口(如
IExecutable)再让外观调用——外观的价值正在于处理异构子系统,强行统一反而增加间接层和误用风险
真正难的从来不是写出一个能编译的 Facade 类,而是判断哪些交互确实该被收束、哪些状态该被隐藏、以及谁该负责销毁那些子系统对象——这些决策一旦定错,后续维护成本会指数级上升。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











