← 返回题目列表

如何手写一个外观模式?Java 代码怎么实现?

高频 中等 第 3 / 25 题 更新于 2026/07/28
外观模式Java代码实现服务聚合设计模式

简化版

手写外观模式就是定义一个 Facade 类,把多个子系统注入进来,对外暴露少量粗粒度方法,内部按顺序调用子系统完成复杂流程。客户端只依赖 Facade,不直接依赖多个子系统。

详细版

示例结构:

class OrderFacade {
    private final UserService userService;
    private final StockService stockService;
    private final OrderService orderService;

    OrderResult createOrder(CreateOrderRequest request) {
        userService.check(request.userId());
        stockService.lock(request.items());
        return orderService.create(request);
    }
}

客户端:

OrderResult result = orderFacade.createOrder(request);

实现重点:

  1. Facade 对外接口要少而清晰;
  2. 子系统仍负责具体能力;
  3. Facade 负责流程编排和结果聚合;
  4. 不要把所有业务逻辑都塞进 Facade;
  5. 复杂事务、幂等、异常处理要有明确策略。

完整版教学

一、先准备几个子系统

class UserService {
    void checkUser(Long userId) {
        System.out.println("校验用户");
    }
}

class StockService {
    void lockStock(List<Long> itemIds) {
        System.out.println("锁定库存");
    }
}

class OrderService {
    Long createOrder(CreateOrderRequest request) {
        System.out.println("创建订单");
        return 1001L;
    }
}

这些子系统分别负责自己的能力。

二、定义外观类

class OrderFacade {
    private final UserService userService;
    private final StockService stockService;
    private final OrderService orderService;

    OrderFacade(UserService userService,
                StockService stockService,
                OrderService orderService) {
        this.userService = userService;
        this.stockService = stockService;
        this.orderService = orderService;
    }

    Long createOrder(CreateOrderRequest request) {
        userService.checkUser(request.getUserId());
        stockService.lockStock(request.getItemIds());
        return orderService.createOrder(request);
    }
}

Facade 对外暴露的是一个业务入口,内部隐藏了子系统调用顺序。

三、客户端只调用外观

OrderFacade facade = new OrderFacade(
    new UserService(),
    new StockService(),
    new OrderService()
);

Long orderId = facade.createOrder(request);

客户端不需要知道创建订单前要先校验用户、再锁库存。

这降低了客户端复杂度。

四、Spring 项目中的写法

实际项目里通常这样写:

@Service
public class OrderFacade {
    private final UserService userService;
    private final StockService stockService;
    private final OrderService orderService;

    public OrderFacade(UserService userService,
                       StockService stockService,
                       OrderService orderService) {
        this.userService = userService;
        this.stockService = stockService;
        this.orderService = orderService;
    }

    public OrderResult createOrder(CreateOrderCommand command) {
        userService.check(command.userId());
        stockService.lock(command.items());
        return orderService.create(command);
    }
}

Facade 常常位于 Controller 和多个领域服务之间。

五、外观类里应该写什么

Facade 适合放:

  • 参数转换;
  • 子系统调用顺序;
  • 结果聚合;
  • 统一异常转换;
  • 事务边界;
  • 调用日志;
  • 权限校验入口。

但复杂业务规则最好放在领域服务或子系统里,不要把 Facade 写成全能类。

六、常见误区与追问

实现质量要从用例原子性和依赖方向检查。一次创建订单涉及校验、扣库存、落单3步,Facade 应清楚定义哪一步失败会回滚、哪一步可重试,而不是只顺序调用三个方法。客户端只依赖 Facade,子系统之间也不应为了这个入口反向依赖 Facade。

检查维度判定依据
Facade编排步骤、定义事务和返回契约
Subsystem保留自身能力与业务不变量

易错点:把三个调用放进一个方法只是起点,失败边界和结果契约才决定实现是否可用。

  • 误区:Facade 必须是单例。 外观模式与实例数量无关,依赖注入容器可按实际生命周期管理。
  • 追问:事务注解应该放哪里? 单体应用中可放用例级 Facade,但要避免把远程调用包进长数据库事务。
  • 误区:Facade 可以捕获并吞掉所有异常。 应转换为稳定的用例错误,同时保留日志和可诊断原因。
  • 追问:返回底层实体合适吗? 对外更适合稳定 DTO,避免调用方依赖子系统内部模型。
  • 追问:如何写实现测试? 验证3步调用顺序、失败短路、事务结果和对外错误映射。

七、加强记忆

手写外观模式就是写一个统一入口类,让客户端调用它,它再调用多个子系统完成复杂流程。Facade 负责收口和编排,子系统负责真实能力;边界清楚,代码才不会变成大杂烩。