外观模式, Facade Pattern
看到"门面"这个词,大家一定都觉得很熟悉。日常生活中的"门面"就是我们买东西的地方,它跟各种商品的生产商打交道,收集商品后再卖给我们。如果没有"门面",我们将不得不直接跟各种各样的生产商买商品。
Facade 模式正是这样一个"门面":我们本来需要与后台的多个类或接口打交道,而 Facade 模式在客户端和后台之间插入一个中间层——门面,这个门面跟后台的多个类或接口打交道,客户端只需要跟门面打交道即可。
Facade 类是一个简化的用户接口,它和后台中的多个类产生依赖关系,而客户类则只跟 Facade 类产生依赖关系。后台的开发者熟悉自己开发的各个类,容易解决与多个类的依赖关系;而使用者不太熟悉后台的各个类,通过 Facade 可以大大降低使用难度。
示例:电商下单流程
classDiagram
class Client
class OrderFacade {
+placeOrder(userId, productId, amount)
}
class InventoryService {
+checkStock(productId)
}
class PaymentService {
+charge(userId, amount)
}
class OrderService {
+createOrder(userId, productId)
}
class NotificationService {
+sendConfirmation(userId)
}
Client --> OrderFacade
OrderFacade --> InventoryService
OrderFacade --> PaymentService
OrderFacade --> OrderService
OrderFacade --> NotificationService
完成一次下单需要依次调用库存服务、支付服务、订单服务和通知服务。没有 Facade 时,调用方必须自己了解并编排这四个步骤:
// 调用方需要知道正确的调用顺序,且任何地方下单都要重复这段逻辑
inventoryService.checkStock(productId);
paymentService.charge(userId, amount);
orderService.createOrder(userId, productId);
notificationService.sendConfirmation(userId);
引入 OrderFacade,将编排逻辑封装在内部:
public class OrderFacade {
private final InventoryService inventoryService;
private final PaymentService paymentService;
private final OrderService orderService;
private final NotificationService notificationService;
public void placeOrder(String userId, String productId, BigDecimal amount) {
inventoryService.checkStock(productId);
paymentService.charge(userId, amount);
orderService.createOrder(userId, productId);
notificationService.sendConfirmation(userId);
}
}
客户端调用:
orderFacade.placeOrder(userId, productId, amount);
调用方完全不需要了解内部有几个步骤、顺序如何、涉及哪些服务——这正是 Facade 的价值所在。
现实中的 Facade:企业 Portal
企业内部通常有很多应用系统——OA、HR、财务、CRM 等,不同系统完成不同功能。企业一般会做一个 Portal 页面,用户在这里可以选择进入哪个子系统,不需要记忆各系统的 URL。
这正是 Facade 模式在产品层面的体现:
| 角色 | 对应 |
|---|---|
| Client | 用户 |
| Facade | Portal 页面 |
| Subsystems | OA、HR、财务等各子系统 |
不过 Portal 更多是 UI 层面的"门面",代码中的 Facade 通常还会做调用编排——不只是提供入口列表,还会把多个子系统的调用封装成一个简单方法(如先调 A,再调 B,最后调 C)。两者概念一脉相承,Portal 是 Facade 思想在系统设计层面的自然延伸。