外观模式是为复杂子系统提供统一简化接口的结构型设计模式,用于解耦客户端与子系统、降低依赖、提升易用性,典型应用于多子系统协作、遗留系统集成及分层架构入口等场景。

外观模式不是“加一层壳就完事”,它解决的是第三方库调用中真实存在的耦合、重复初始化、错误传播和测试困难问题。直接在业务逻辑里 new GoogleClient() 或硬编码 file_get_contents('https://api.example.com/...'),迟早会拖垮维护节奏。
什么时候该用 Facade 而不是直接 new 一个客户端?
当出现以下任意一种情况时,Facade 就不是可选项,而是止损点:
- 同一组 API 调用在多个 Controller / Service 中重复出现(比如每次都要手动构造 token、设置 header、解析 JSON、处理 401/429)
- 第三方 SDK 的初始化成本高(如
Google\Client构造时加载大量配置、验证证书、连接 discovery service) - 你发现单元测试里不得不 mock 七八个依赖,只为了测一个搜索方法
- 团队新人第一次改支付回调逻辑,花了半天才搞懂
$paypal->getTransaction()->getDetails()->getItems()[0]->getName()这一长串是谁的责任
Facade 类里不能出现 new,但依赖怎么来?
外观类本身必须是无状态的,它的职责是编排,不是创建。构造函数里只接收已实例化的子系统对象,且类型必须是接口而非具体类:
class PaymentFacade
{
public function __construct(
private PaymentGatewayInterface $gateway,
private LoggerInterface $logger,
private CacheInterface $cache
) {}
}
如果你看到 new StripeClient() 出现在 PaymentFacade::__construct() 或任何公开方法里,说明它已经越界,该拆成工厂或交给 DI 容器处理。PHP 8.1+ 推荐用命名参数 + 构造函数属性提升,让依赖一目了然。
Facade 方法签名要克制,别变成“万能胶”
常见反模式:process($data, $mode = 'sync', $retry = true, $notify = false, $trace = null) —— 这已经不是 Facade,是配置黑洞。
正确做法是按场景拆方法,每个方法只做一件事,并暴露关键失败点:
chargeCard(string $cardToken, float $amount): ChargeResultrefundCharge(string $chargeId, ?float $amount = null): RefundResultverifyWebhook(array $payload, string $signature): bool
返回值用明确的 DTO(如 ChargeResult),而不是 array 或 stdClass。这样调用方不用猜键名,IDE 能补全,类型检查不漏。
Facade 和子系统之间的边界容易模糊
最容易被忽略的一点:Facade 不负责重试、熔断、降级——这些属于中间件或领域服务层。Facade 只保证「把请求发出去、把响应拿回来、把原始错误转成领域语义错误」。
比如第三方 API 返回 503 Service Unavailable,Facade 不应该自己 sleep(1) 再重试,而应抛出 ThirdPartyUnavailableException,由上层决定是重试、走缓存还是返回友好提示。
同样,日志记录只记关键上下文(如请求 ID、金额、目标商户),不记完整 raw response body;敏感字段(如卡号、token)必须脱敏,这要在 Facade 入口就过滤,别等日志组件去处理。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











