外观模式
问题
什么是外观(门面)模式?在 Java 项目中如何应用?
答案
核心思想
为子系统中的一组复杂接口提供一个统一的高层接口,客户端只需与外观类交互,不用关心子系统的内部细节。
实现示例
外观模式 - 下单流程
// 复杂的子系统
@Service
public class InventoryService {
public boolean checkStock(String productId, int qty) { /* ... */ return true; }
public void deductStock(String productId, int qty) { /* ... */ }
}
@Service
public class PaymentService {
public PayResult pay(String orderId, BigDecimal amount) { /* ... */ return null; }
}
@Service
public class ShippingService {
public String createShipment(String orderId, String address) { /* ... */ return ""; }
}
// 外观类:封装下单的复杂流程,对外暴露简单接口
@Service
public class OrderFacade {
@Autowired private InventoryService inventoryService;
@Autowired private PaymentService paymentService;
@Autowired private ShippingService shippingService;
/**
* 一键下单:封装库存检查 → 支付 → 发货的复杂流程
*/
public OrderResult placeOrder(OrderRequest request) {
// 1. 检查库存
if (!inventoryService.checkStock(request.getProductId(), request.getQuantity())) {
return OrderResult.fail("库存不足");
}
// 2. 扣减库存
inventoryService.deductStock(request.getProductId(), request.getQuantity());
// 3. 发起支付
PayResult payResult = paymentService.pay(request.getOrderId(), request.getAmount());
if (!payResult.isSuccess()) {
return OrderResult.fail("支付失败");
}
// 4. 创建物流
String trackingNo = shippingService.createShipment(
request.getOrderId(), request.getAddress());
return OrderResult.success(trackingNo);
}
}
// Controller 只需调用外观类
@RestController
public class OrderController {
@Autowired
private OrderFacade orderFacade;
@PostMapping("/orders")
public OrderResult createOrder(@RequestBody OrderRequest request) {
return orderFacade.placeOrder(request);
}
}
Java/Spring 中的典型应用
| 应用 | 说明 |
|---|---|
| SLF4J | 统一日志门面,屏蔽底层日志框架差异 |
Spring JdbcTemplate | 封装 JDBC 繁琐的连接/关闭/异常处理 |
Spring RestTemplate | 封装 HTTP 调用细节 |
| BFF 层 | 后端聚合多个微服务,提供统一 API 给前端 |
| Service 层 | 封装多个 DAO 的调用逻辑 |
外观模式 ≈ 分层封装
Java 项目的 Controller → Service → DAO 三层架构天然就是外观模式的应用:
- Service 是 DAO 的外观
- Controller 是 Service 的外观
- BFF 是多个微服务的外观
常见面试问题
Q1: 外观模式和代理模式的区别?
答案:
| 维度 | 外观模式 | 代理模式 |
|---|---|---|
| 目的 | 简化多个子系统的复杂接口 | 控制对单个对象的访问 |
| 对象数 | 封装多个子系统 | 代理一个目标对象 |
| 接口 | 提供新的简化接口 | 与目标对象接口相同 |
Q2: 外观模式的缺点?
答案:
- 外观类可能变成"上帝类"(God Class),承担过多职责
- 新增子系统功能需要修改外观类
- 不符合开闭原则
解决方案:将外观类拆分为多个领域外观(如 OrderFacade、UserFacade),每个只负责一个业务领域。