外观模式是面向业务流程的封装,用于简化长调用链、固化执行顺序、统一异常处理;orderfacade应为具体类,仅注入核心依赖,不管理子系统生命周期,且必须转换底层异常为语义明确的业务异常并保留innerexception。

外观模式不是用来“炫技”的,是当子系统调用链太长、顺序太脆、错误处理太散时,你不得不拉一道墙把混乱挡在后面——它本质是面向业务流程的封装,不是面向接口的抽象。
什么时候该写 OrderFacade 而不是 IOrderFacade
初期别急着抽象接口。真实项目里,OrderFacade 是具体类,构造函数只接收真正参与下单流程的依赖:
-
IOrderService、IPaymentGateway、IInventoryClient这三个必须传入; -
ILogger、IConfiguration、IDistributedCache这类通用服务,用构造函数注入反而让职责模糊; - 如果某个子系统(比如物流)只在「发货成功后」才介入,那它不该出现在
PlaceAndPayOrder()的主路径里,更不该塞进构造函数。
PlaceAndPayOrder() 里为什么必须重抛异常
子系统各自抛自己的异常类型(SqlException、HttpRequestException、TimeoutException),上层根本没法做统一判断。外观层必须做两件事:
- 捕获底层异常,转成业务语义明确的类型,比如
OrderProcessingException; - 保留原始异常作为
InnerException,否则排查时会丢掉关键堆栈; - 不要吞掉异常或只打日志就返回
null——这会让调用方误以为流程成功。
为什么 HomeTheaterFacade.WatchMovie() 不该暴露 DvdPlayer 实例
家庭影院示例里常见错误:把 DvdPlayer 字段设为 public 或加 get 访问器。后果是:
- 客户端绕过外观直接调
_dvd.Play(),破坏了灯光→投影→播放的协作顺序; - 后续想加播放前自动检测碟片状态,就得改所有直接调用点;
- 外观失去“控制权”,变成摆设——它本该是唯一入口,不是中转站。
最易被忽略的一点:外观类不负责子系统的生命周期管理。它只持有引用,不创建也不释放。如果子系统需要 IDisposable,那是 DI 容器或调用方的事,外观里写 Dispose() 反而容易引发双重释放或漏释放。











