重构手法——函数行为重塑类 | 语意 | 用预检查替换异常

66 阅读2分钟

简介

"用预检查替换异常"是一种防御性编程的重构手法。通过在可能引发异常的场景前添加前置条件检查,将运行时异常转换为可控的逻辑分支,提高代码健壮性和可维护性。

针对的症状(代码坏味道)

  • 使用异常处理正常业务流程控制
  • 频繁try-catch基础验证逻辑
  • 异常处理成本高于业务逻辑本身

用预检查替换异常的详细步骤

  1. 识别异常触发点
    • 查找代码中可能抛出异常的敏感操作
    • 分析异常触发的前置条件
  2. 创建验证方法
    • 将异常触发条件提取为独立验证方法
    • 确保验证方法的无副作用性
  3. 添加前置检查
    • 在调用敏感操作前执行验证
    • 使用验证结果控制流程
  4. 重构异常处理
    • 将catch块转换为条件判断
    • 移除不必要的try-catch结构

示例

重构前代码:

class AccountManager {
    void transfer(Account from, Account to, double amount) {
        try {
            from.withdraw(amount);
            to.deposit(amount);
        } catch (InsufficientFundsException e) {
            System.out.println("Transfer failed: " + e.getMessage());
        }
    }
}

重构步骤:

  1. 创建预检查方法:

    private boolean canTransfer(Account from, double amount) {
        return from.getBalance() >= amount;
    }
    
  2. 重构转账逻辑:

    class AccountManager {
        void transfer(Account from, Account to, double amount) {
            if (!canTransfer(from, amount)) {
                System.out.println("Transfer failed: Insufficient funds");
                return;
            }
    
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
    

练习

基础练习题

  1. 简单参数预检查

    // 重构前
    class StringUtils {
        String safeSubstring(String s, int begin) {
            try {
                return s.substring(begin);
            } catch (IndexOutOfBoundsException e) {
                return "";
            }
        }
    }
    

进阶练习题

  1. 复合条件检查

    // 重构前
    class FileConverter {
        void convert(File input, String format) {
            try {
                validateFormat(format);
                processConversion(input);
            } catch (InvalidFormatException | ConversionFailureException e) {
                log.error("Conversion failed", e);
            }
        }
    }
    

综合拓展练习题

  1. 复杂业务规则检查

    // 重构前
    class LoanService {
        void approveLoan(Application app) {
            try {
                checkCreditHistory(app);
                verifyCollateral(app);
                processApproval(app);
            } catch (LoanException e) {
                app.reject(e.getReason());
            }
        }
    }
    

代码审查要点

  1. 优点:

    • 减少异常处理开销
    • 提前暴露业务规则问题
    • 改善代码可读性
  2. 潜在问题:

    • 确保预检查与业务逻辑原子性
    • 避免重复验证逻辑
    • 保持验证逻辑与业务逻辑同步