外观模式:统一接口、简化子系统
外观模式,说白了就是给你的系统装一个「前台接待」。
我刚开始做架构时,经常遇到这样的场景:一个业务操作需要调用三四个子系统,每个子系统又有自己的初始化顺序、参数校验、异常处理。调用方苦不堪言,代码里全是「先调A,再调B,如果B失败要回滚A...」这种逻辑。
嗯,外观模式就是来解决这个问题的。
什么是外观模式?
外观模式(Facade Pattern)属于结构型设计模式。它为一组复杂的子系统接口提供一个统一的、更高级的接口。调用方只需要跟这个外观类打交道,不需要关心子系统内部怎么折腾。
我个人的理解:外观模式就是「封装变化,暴露稳定」。子系统可以随便改,只要外观接口不变,调用方就不用动代码。
| 角色 | 说明 |
|---|---|
| 外观类(Facade) | 对外提供统一接口,内部协调子系统 |
| 子系统类(Subsystem) | 实现具体功能,不感知外观类的存在 |
| 客户端(Client) | 只调用外观类的方法 |
一个简单的例子
假设我们有一个订单系统,下单时需要做三件事:校验库存、扣减余额、发送通知。
没有外观模式时,客户端代码是这样的:
// 客户端直接操作子系统
InventoryService inventory = new InventoryService();
PaymentService payment = new PaymentService();
NotificationService notification = new NotificationService();
if (inventory.checkStock("item123", 1)) {
payment.deduct("user456", 99.0);
notification.send("user456", "下单成功");
}
看着还行?那如果再加一个「积分赠送」呢?再加一个「物流预分配」呢?调用方代码会越来越臃肿。
用外观模式改造一下:
// 外观类
public class OrderFacade {
private InventoryService inventory;
private PaymentService payment;
private NotificationService notification;
public OrderFacade() {
this.inventory = new InventoryService();
this.payment = new PaymentService();
this.notification = new NotificationService();
}
public void placeOrder(String itemId, String userId, double amount) {
if (!inventory.checkStock(itemId, 1)) {
throw new RuntimeException("库存不足");
}
payment.deduct(userId, amount);
notification.send(userId, "下单成功");
}
}
// 客户端代码
OrderFacade facade = new OrderFacade();
facade.placeOrder("item123", "user456", 99.0);
你看,客户端只需要一行调用。子系统再怎么变,客户端都不用改。
实战:API网关设计
外观模式在企业级应用中最典型的场景就是API网关。我在项目中遇到过好几次这样的需求:微服务多了之后,客户端要调用好几个接口才能完成一个功能,而且每个接口的认证、限流、日志都得单独处理。
API网关本质上就是一个外观模式——它把多个微服务的接口统一暴露给客户端,同时把认证、限流、日志这些横切关注点集中处理。
下面是一个简化版的API网关设计:
// 网关外观类
public class ApiGatewayFacade {
private AuthService authService;
private RateLimiter rateLimiter;
private Logger logger;
private UserService userService;
private OrderService orderService;
public ApiGatewayFacade() {
this.authService = new AuthService();
this.rateLimiter = new RateLimiter();
this.logger = new Logger();
this.userService = new UserService();
this.orderService = new OrderService();
}
public String handleRequest(String token, String apiName, Map<String, Object> params) {
// 1. 认证
if (!authService.authenticate(token)) {
return "401 Unauthorized";
}
// 2. 限流
if (!rateLimiter.allowRequest(token)) {
return "429 Too Many Requests";
}
// 3. 路由到具体服务
String result;
switch (apiName) {
case "getUserInfo":
result = userService.getUserInfo((String) params.get("userId"));
break;
case "createOrder":
result = orderService.createOrder(params);
break;
default:
result = "404 Not Found";
}
// 4. 日志记录
logger.log(token, apiName, result);
return result;
}
}
外观模式的核心逻辑
为了让你更直观地理解外观模式在API网关中的运作方式,我画了一张图:
这张图很清楚地展示了:客户端只跟API网关交互,网关内部协调多个微服务。每个微服务只关心自己的事,网关负责「穿针引线」。
外观模式的优缺点
优点很明显:
- 简化调用:客户端不需要知道子系统内部细节
- 降低耦合:客户端和子系统之间没有直接依赖
- 集中控制:认证、限流、日志等横切关注点统一管理
缺点也要心里有数:
- 外观类可能变成上帝类:如果什么逻辑都往里塞,外观类会变得臃肿不堪
- 不符合开闭原则:新增子系统可能需要修改外观类
什么时候用外观模式?
我个人总结了三类场景:
- 子系统太多太杂:客户端需要跟多个子系统交互,代码里全是编排逻辑
- 需要统一入口:比如API网关、SDK封装、第三方服务对接
- 子系统需要分层:每一层对外暴露一个外观类,内部细节对外隐藏
你想想看,如果你的代码里出现了「先调A,再调B,然后调C,如果C失败要回滚A和B」这种逻辑,而且出现在多个地方——嗯,这就是外观模式该出场的时候了。
外观模式的核心思想其实很简单——给复杂系统装一个「前台」。调用方不用知道后台有多少部门在协作,只需要跟前台说一声「我要办这个事」就行了。API网关就是这种思想在企业级架构中的最佳实践。
公众号:蓝海资料掘金营,微信deep3321