本文目录导读:

Java 代码重构中,调用流程的“规整”核心目标是:消除副作用、降低耦合、提升可读性和可测试性,规整后的调用流程通常呈现为清晰的步骤链或分层结构。
以下是具体的方法论和实操技巧,分为思想原则、具体模式、代码示例和反例对比四个部分。
核心思想原则
在动手改代码前,先建立这几个思维模型:
- 单一职责:一个方法只做一件事,如果调用流程里夹杂了日志、缓存、权限检查、数据转换,就需要拆开。
- 依赖倒置:高层模块(业务逻辑)不应该依赖低层模块(具体数据库、第三方API),两者都应该依赖抽象(接口)。
- Tell, Don‘t Ask:告诉对象该做什么,而不是不断查询它的内部状态再做决定。
- 最小知识原则(Law of Demeter):一个对象只与直接朋友通信,避免出现
a.getB().getC().doSomething()这种长链调用。
具体规整模式与技巧
分层架构:明确关注点分离
这是最基础也是最重要的规整方式,经典的三层结构(表现层、业务层、持久层)可以升级为:
- Controller:接收请求、校验参数、返回响应。不写业务逻辑。
- Service:编排业务流程、事务控制。不直接操作数据库。
- Repository/DAO:数据访问。不写业务判断。
- Domain Model:包含核心业务规则和状态变更逻辑。
规整要点:确保调用流是 Controller -> Service -> Repository,不能出现跨层调用(如 Controller 直接调用 Repository)。
使用设计模式简化流转
| 场景 | 推荐的规整模式 | 说明 |
|---|---|---|
| 多步骤流程,且步骤可能变化 | 模板方法模式 | 定义算法骨架,子类实现细节。 |
| 复杂的条件逻辑(多个if-else) | 策略模式 | 将每个分支封装成一个策略类,通过上下文调用。 |
| 需要在不修改核心流程时添加额外动作 | 装饰器模式 / 责任链模式 | 如:日志、缓存、鉴权。 |
| 需要将异步回调链串起来 | CompletableFuture + 流水线风格 | 替代回调地狱。 |
消除“传参链”和“中间变量”
- 问题:A方法调用B,B需要参数X、Y、Z,C方法又需要这些。
- 规整方案:
- 将相关参数封装为上下文对象(Context / DTO)。
- 或者使用 Fluent Interface(流式调用),让代码读起来像一句话。
异常处理与边界定义
- 不要在Service层吞掉异常后返回null,这会让调用方不知所措。
- 规整:定义业务异常(
BusinessException),在流程边界统一处理,调用链路上明确 throws 声明。
实战代码对比:从混乱到规整
假设有一个场景:用户下单购买商品,需要验证库存、计算价格、保存订单、发送通知。
❌ 混乱版本(非规整调用流程)
// Controller 里写了太多东西
@RestController
public class OrderController {
@Autowired
private OrderService orderService; // 假设存在
@PostMapping("/order")
public String createOrder(@RequestBody OrderRequest request) {
// 1. 在Controller里校验库存 (不应该)
if (orderService.checkStock(request.getProductId()) < request.getQuantity()) {
return "库存不足";
}
// 2. 计算价格 (应该交给Service)
double price = orderService.calcPrice(request.getProductId(), request.getQuantity());
// 3. 扣减库存 (耦合了)
boolean deducted = orderService.deductStock(request.getProductId(), request.getQuantity());
if (!deducted) {
return "扣库存失败";
}
// 4. 保存订单并通知 (混合逻辑)
Order order = new Order();
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
order.setTotalPrice(price);
orderService.save(order);
orderService.sendNotification(order); // 发送通知混在流程里
return "成功";
}
}
问题:控制器过重、副作用混乱、事务边界不清、无法单元测试。
✅ 规整版本(清晰调用流程)
Step 1: 定义清晰的入参和上下文对象
// 使用 Builder 模式封装参数
public class CreateOrderCommand {
private Long productId;
private Integer quantity;
private Long userId;
// getter, builder...
}
Step 2: 拆分 Service 职责
@Service
public class OrderService {
@Autowired
private ProductService productService; // 负责库存和价格
@Autowired
private OrderRepository orderRepository;
@Autowired
private NotificationService notificationService; // 异步逻辑
@Transactional // 事务边界清晰
public Order createOrder(CreateOrderCommand command) {
// 1. 验证业务规则 (调用领域服务)
Product product = productService.validateAndGetProduct(command.getProductId());
// 2. 计算价格 (纯业务,无副作用)
Money totalPrice = product.calculatePriceForQuantity(command.getQuantity());
// 3. 创建订单领域对象 (领域模型封装逻辑)
Order order = Order.create(
command.getUserId(),
product.getId(),
command.getQuantity(),
totalPrice
);
// 4. 保存订单 (Repository 只做持久化)
order = orderRepository.save(order);
// 5. 扣减库存 (作为领域事件或服务调用)
productService.deductStock(product.getId(), command.getQuantity());
// 6. 发送通知 (异步非阻塞,不阻塞主流程)
notificationService.sendOrderCreatedAsync(order);
return order;
}
}
Step 3: Controller 只负责接和转
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/order")
public ResponseEntity<OrderResponse> createOrder(@RequestBody @Valid CreateOrderCommand command) {
// Controller 只做参数校验和调用,无业务逻辑
Order order = orderService.createOrder(command);
return ResponseEntity.ok(OrderResponse.from(order));
}
}
规整调用流程的Checklist
在重构时,可以对照以下清单逐条检查:
- [ ] 没有 null 引用传递:除非明确语义,否则返回
Optional或抛出异常。 - [ ] 方法参数不超过3个:超过则封装为对象。
- [ ] 没有深层嵌套的 if-else:使用 early return、策略模式或枚举。
- [ ] 循环内没有数据库调用:使用批量查询(
IN查询)。 - [ ] 没有在循环内部调用远程 API:预加载或合并请求。
- [ ] 异步调用有明确边界:使用
@Async、消息队列或CompletableFuture,并在文档中说明。 - [ ] 不存在 “A调用B,B调用C,C又调回A”:这是循环依赖,必须使用事件或中间层解耦。
进阶:引入领域事件
如果调用流程中有很多“后置处理”(比如发邮件、更新ES、记录日志),可以通过事件发布来进一步规整:
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public Order createOrder(CreateOrderCommand cmd) {
// 核心流程...
Order order = orderRepository.save(Order.create(...));
// 发布事件,解耦后置操作
eventPublisher.publishEvent(new OrderCreatedEvent(order));
return order;
}
}
// 专门监听事件
@Component
public class OrderEventListener {
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) // 事务提交后执行
public void handleOrderCreated(OrderCreatedEvent event) {
// 发送邮件、通知、更新缓存等
notificationService.send(...);
}
}
好处:主流程(创建订单)永远不会被邮件发送、ES同步等逻辑堵塞或影响。
规整Java调用流程,本质上是把“面条式代码”梳理成“管道式代码”:
- 输入 → 校验 → 业务处理 → 持久化 → 后置事件 → 输出
- 每一层只做一件事,每个方法只做一件事。
- 如果发现一个方法里既有 if-else 又有 try-catch 还有 Lambda 链,就是重构的信号。
不要为了规整而过度抽象,对于简单 CRUD(增加、读取、更新、删除),直接穿透调用即可,规整的度在于:当需求变更时,你修改的代码范围最小,且能通过单元测试覆盖。