P88 面试必问:外观和组合模式到底怎么选?
面试题:外观模式(Facade)和组合模式(Composite)有什么区别?各自怎么用?
1. 两个模式定位完全不同
| 对比项 | 外观模式 Facade | 组合模式 Composite |
|---|---|---|
| 目的 | 简化接口:给复杂子系统提供统一入口 | 树形结构:让叶子与容器对象一致对待 |
| 解决的问题 | 客户端调用太繁琐/耦合子系统 | 整体-部分层次结构(树) |
| 结构 | 门面类包装多个子系统 | 叶子 + 容器,递归组合 |
| 典型场景 | Service 层聚合多个组件、SDK 封装 | 菜单树、文件系统、组织架构树 |
2. 外观模式(Facade)
text
客户端 → Facade(门面)→ 子系统 A / B / Cjava
// 下单门面:一个方法搞定 校验 + 库存 + 支付 + 通知
public class OrderFacade {
private final StockService stockService;
private final PayService payService;
private final NotifyService notifyService;
public void createOrder(Order order) {
stockService.deduct(order);
payService.pay(order);
notifyService.send(order);
}
}价值:
- 客户端只依赖 Facade,不直接碰一堆子系统;
- 降低耦合、简化调用;
- 典型:Service 层对外提供粗粒度方法、SDK 门面。
3. 组合模式(Composite)
text
Component(抽象:叶子 + 容器共同接口)
├── Leaf(叶子:文件/菜单项)
└── Composite(容器:目录/菜单,可包含子节点)java
public interface MenuComponent {
void show();
}
// 叶子:菜单项
class MenuItem implements MenuComponent { ... }
// 容器:菜单(可以继续装菜单项或子菜单)
class Menu implements MenuComponent {
private List<MenuComponent> children = new ArrayList<>();
public void add(MenuComponent c) { children.add(c); }
public void show() { children.forEach(MenuComponent::show); }
}价值:
- 客户端用统一接口操作叶子与容器,递归结构天然适配树;
- 典型:文件系统、菜单树、组织架构、分类树。
4. 怎么选
- 想简化一堆复杂组件的调用 → 外观模式;
- 想表达树形整体-部分结构,且希望叶子/容器一致处理 → 组合模式;
- 两者不冲突,可以组合使用:外观聚合多个服务,组合表达层级数据。
5. 加分点
- 说出"外观模式重在封装与解耦,组合模式重在递归树结构",一句话区分;
- 举源码例子:MyBatis 的 SqlSession(外观)、Spring 的 CompositeCache 等;
- 追问"装饰器和组合模式区别":装饰器是"包装增强",组合是"树形聚合"。
一句话总结
外观模式给复杂子系统提供一个简化入口(重在解耦),组合模式用树形结构让叶子与容器统一处理(重在递归层级);按"要简化调用还是要表达树结构"来选择,二者可叠加使用。