Java重构调用流程如何规整

wen java案例 32

本文目录导读:

Java重构调用流程如何规整

  1. 核心思想原则
  2. 具体规整模式与技巧
  3. 实战代码对比:从混乱到规整
  4. 规整调用流程的Checklist
  5. 进阶:引入领域事件

Java 代码重构中,调用流程的“规整”核心目标是:消除副作用、降低耦合、提升可读性和可测试性,规整后的调用流程通常呈现为清晰的步骤链分层结构

以下是具体的方法论和实操技巧,分为思想原则具体模式代码示例反例对比四个部分。

核心思想原则

在动手改代码前,先建立这几个思维模型:

  1. 单一职责:一个方法只做一件事,如果调用流程里夹杂了日志、缓存、权限检查、数据转换,就需要拆开。
  2. 依赖倒置:高层模块(业务逻辑)不应该依赖低层模块(具体数据库、第三方API),两者都应该依赖抽象(接口)。
  3. Tell, Don‘t Ask:告诉对象该做什么,而不是不断查询它的内部状态再做决定。
  4. 最小知识原则(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

在重构时,可以对照以下清单逐条检查:

  1. [ ] 没有 null 引用传递:除非明确语义,否则返回 Optional 或抛出异常。
  2. [ ] 方法参数不超过3个:超过则封装为对象。
  3. [ ] 没有深层嵌套的 if-else:使用 early return、策略模式或枚举。
  4. [ ] 循环内没有数据库调用:使用批量查询(IN 查询)。
  5. [ ] 没有在循环内部调用远程 API:预加载或合并请求。
  6. [ ] 异步调用有明确边界:使用 @Async、消息队列或 CompletableFuture,并在文档中说明。
  7. [ ] 不存在 “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(增加、读取、更新、删除),直接穿透调用即可,规整的度在于:当需求变更时,你修改的代码范围最小,且能通过单元测试覆盖

抱歉,评论功能暂时关闭!