You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

REST服务中基于多态重构条件判断代码的实现咨询

重构建议:用多态+策略模式取代双重维度(Type+Region)的条件判断

问题描述

我正尝试清理并重构我的服务代码,当前代码如下:

public void generateBalance(Receipt receipt) { 
  if (receipt.getType().equals(X) && receipt.getRegion.equals(EMEA)) { 
    // do something to the receipt that's passed 
  } else if (receipt.getType().equals(Y)) { 
    // do something to the receipt 
  } else if (receipt.getRegion.equals(APAC)) { 
    // call an external API and update the receipt 
  }.... 
  ... 
  // finally dataStore.save(receipt); 
}

该主服务类中存在大量基于传入对象(Receipt)的type或region字段的条件判断逻辑。我希望采用「以多态取代条件表达式」的设计模式进行重构,但不确定该模式如何在服务类中应用——当前我的REST handler会调用此服务类。此外,我还困惑于如何针对receiptType和region两个维度实现多态,以及能否在不同服务中完成对receipt的所有更新操作后,在统一位置(比如基类)保存receipt。我不知该从何入手,感谢您的帮助!


分步解决方案

一、核心思路:用策略+多态应对双重维度分支

你的场景是典型的双重条件分支(Type + Region),直接用单继承多态会不够灵活,结合策略模式+多态是最优解——既符合"取代条件表达式"的重构原则,又能轻松应对两个维度的变化。

二、具体重构步骤

1. 定义处理接口,抽象核心行为

先创建一个接口,把原来每个条件分支里的逻辑抽象成两个核心方法:匹配判断、业务处理。

public interface ReceiptBalanceProcessor {
    // 判断当前处理器是否适配传入的Receipt
    boolean matches(Receipt receipt);
    // 执行具体的余额生成逻辑
    void processBalance(Receipt receipt);
}

2. 为每个条件分支实现独立处理器

把原来每个if/else块里的逻辑拆成单独的实现类,职责单一,便于维护:

  • 处理「Type=X且Region=EMEA」的情况:
public class XTypeEMEAProcessor implements ReceiptBalanceProcessor {
    @Override
    public boolean matches(Receipt receipt) {
        return X.equals(receipt.getType()) && EMEA.equals(receipt.getRegion());
    }

    @Override
    public void processBalance(Receipt receipt) {
        // 原第一个if块中的业务逻辑
    }
}
  • 处理「Type=Y」的情况:
public class YTypeProcessor implements ReceiptBalanceProcessor {
    @Override
    public boolean matches(Receipt receipt) {
        return Y.equals(receipt.getType());
    }

    @Override
    public void processBalance(Receipt receipt) {
        // 原第二个else if块中的业务逻辑
    }
}
  • 处理「Region=APAC」的情况:
public class APACRegionProcessor implements ReceiptBalanceProcessor {
    // 依赖外部API客户端可通过构造注入,避免硬编码
    private final ExternalApiClient apiClient;

    public APACRegionProcessor(ExternalApiClient apiClient) {
        this.apiClient = apiClient;
    }

    @Override
    public boolean matches(Receipt receipt) {
        return APAC.equals(receipt.getRegion());
    }

    @Override
    public void processBalance(Receipt receipt) {
        // 调用外部API并更新receipt的逻辑
        ApiResponse response = apiClient.fetchBalance(receipt.getId());
        receipt.setBalance(response.getBalance());
    }
}

3. 改造原服务类,实现自动匹配处理器

原来的服务类不再写一堆if/else,而是注入所有ReceiptBalanceProcessor实现,通过匹配逻辑找到对应的处理器执行:

@Service // 以Spring为例,其他框架同理
public class ReceiptBalanceService {
    private final List<ReceiptBalanceProcessor> processors;
    private final DataStore dataStore;

    // 构造注入所有处理器和数据存储组件
    public ReceiptBalanceService(List<ReceiptBalanceProcessor> processors, DataStore dataStore) {
        this.processors = processors;
        this.dataStore = dataStore;
    }

    public void generateBalance(Receipt receipt) {
        // 找到第一个匹配的处理器(后续可优化优先级)
        ReceiptBalanceProcessor matchingProcessor = processors.stream()
                .filter(processor -> processor.matches(receipt))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("No processor found for receipt: " + receipt));

        // 执行业务逻辑
        matchingProcessor.processBalance(receipt);
        
        // 统一在这里保存receipt,所有处理器执行完后都走这一步
        dataStore.save(receipt);
    }
}

4. 处理优先级冲突(关键细节)

如果存在多个处理器同时匹配一个Receipt的情况(比如某个Receipt既满足Type=Y又满足Region=APAC),可以给接口增加优先级方法来解决:

public interface ReceiptBalanceProcessor {
    boolean matches(Receipt receipt);
    void processBalance(Receipt receipt);
    // 数字越大优先级越高,默认0
    default int getPriority() {
        return 0;
    }
}

比如让XTypeEMEAProcessor的优先级更高:

@Override
public int getPriority() {
    return 10;
}

然后在服务类里按优先级排序后匹配:

ReceiptBalanceProcessor matchingProcessor = processors.stream()
        .filter(processor -> processor.matches(receipt))
        .sorted((p1, p2) -> Integer.compare(p2.getPriority(), p1.getPriority()))
        .findFirst()
        .orElseThrow(...);

三、和REST Handler的集成

原REST Handler完全不需要修改,依然注入ReceiptBalanceService并调用方法即可,内部多态逻辑对上层完全透明:

@RestController
public class ReceiptController {
    private final ReceiptBalanceService balanceService;

    public ReceiptController(ReceiptBalanceService balanceService) {
        this.balanceService = balanceService;
    }

    @PostMapping("/receipts/balance")
    public ResponseEntity<Void> generateBalance(@RequestBody Receipt receipt) {
        balanceService.generateBalance(receipt);
        return ResponseEntity.ok().build();
    }
}

四、关于统一保存的需求

你提到的「在统一位置保存receipt」已经在ReceiptBalanceService的generateBalance()方法中实现——不管哪个处理器执行完业务逻辑,最后都会走到dataStore.save(receipt),完美满足你的需求。

五、额外优势

  • 扩展性极强:新增Type或Region的处理逻辑时,只需要新增一个ReceiptBalanceProcessor实现类,完全不需要修改原有代码,符合开闭原则。
  • 可测试性更好:每个处理器可以单独写单元测试,不用在一个大方法里覆盖所有分支。
  • 代码更清晰:每个处理器只负责自己的业务逻辑,职责单一,可读性拉满。

内容的提问来源于stack exchange,提问作者mistermims

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 00:47:44