重构手法——对象结构优化类 | 类关系解耦 | 引入解释性变量

64 阅读4分钟

简介

“引入解释性变量”(Introduce Explaining Variable)是一种重要的代码重构技术,它能够显著提升代码的可读性和可维护性。当代码中存在复杂的表达式、条件判断或逻辑运算时,这些代码可能会让阅读者难以理解其具体含义和意图。通过引入解释性变量,将复杂的逻辑拆分成多个步骤,并使用有意义的变量名来存储中间结果,能够使代码的逻辑更加清晰,让开发者更容易理解代码的功能。

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

  • 复杂表达式:代码中包含多个运算符、函数调用或嵌套的表达式,难以直接理解其计算目的。
  • 冗长的条件判断if 语句中的条件判断包含多个逻辑运算符和变量,导致条件判断的含义不清晰。
  • 逻辑难以理解:代码的逻辑复杂,没有明显的结构,使得阅读者需要花费大量时间来分析和理解。

引入解释性变量(Introduce Explaining Variable)的详细步骤

  1. 识别复杂逻辑
    • 在代码中找出那些复杂的表达式、条件判断或逻辑运算,这些部分可能会影响代码的可读性。
  2. 创建解释性变量
    • 为复杂逻辑的各个部分创建有意义的变量名。变量名应该能够清晰地描述其所代表的含义。
  3. 赋值给解释性变量
    • 将复杂逻辑的相应部分赋值给新创建的解释性变量。
  4. 替换原始代码
    • 在原始代码中,用解释性变量替换对应的复杂逻辑部分。
  5. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
    • 手动测试:如果有必要,进行手动测试以验证功能的正确性。
  6. 代码审查
    • 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
    • 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明引入解释性变量的影响。

示例

假设有一个方法用于判断一个日期是否在某个特定的促销时间段内。

原始代码

import java.util.Calendar;

public class PromotionChecker {
    public boolean isInPromotionPeriod(Calendar date) {
        return date.get(Calendar.MONTH) == Calendar.DECEMBER && date.get(Calendar.DAY_OF_MONTH) >= 1 && date.get(Calendar.DAY_OF_MONTH) <= 25;
    }
}

重构步骤

  1. 识别复杂逻辑date.get(Calendar.MONTH) == Calendar.DECEMBER && date.get(Calendar.DAY_OF_MONTH) >= 1 && date.get(Calendar.DAY_OF_MONTH) <= 25 这个条件判断较为复杂,不易理解。
  2. 创建解释性变量:创建 isDecemberisAfterFirstisBeforeTwentyFifth 三个变量。
  3. 赋值给解释性变量:将相应的逻辑赋值给这些变量。
  4. 替换原始代码:用解释性变量替换原始的条件判断。

重构后代码

import java.util.Calendar;

public class PromotionChecker {
    public boolean isInPromotionPeriod(Calendar date) {
        boolean isDecember = date.get(Calendar.MONTH) == Calendar.DECEMBER;
        boolean isAfterFirst = date.get(Calendar.DAY_OF_MONTH) >= 1;
        boolean isBeforeTwentyFifth = date.get(Calendar.DAY_OF_MONTH) <= 25;

        return isDecember && isAfterFirst && isBeforeTwentyFifth;
    }
}

练习

基础练习题

  1. 简单表达式解释

    • 给定以下 Java 代码,calculateTotal 方法中有一个复杂的表达式。请引入解释性变量来提高代码的可读性。
      public class OrderCalculator {
          public double calculateTotal(double price, double taxRate, double discountRate) {
              return price * (1 - discountRate) + price * (1 - discountRate) * taxRate;
          }
      }
      
  2. 条件判断解释

    • 下面的 Java 代码中,isEligibleForDiscount 方法的条件判断较为复杂。请引入解释性变量来清晰表达条件的含义。
    public class DiscountEligibilityChecker {
        public boolean isEligibleForDiscount(int age, int purchaseAmount) {
            return (age >= 60 || purchaseAmount >= 1000) && (age < 18 || purchaseAmount >= 500);
        }
    }
    

进阶练习题

  1. 嵌套逻辑解释

    • 在这段 Java 代码中,processData 方法包含嵌套的逻辑判断。请引入解释性变量来简化逻辑并提高可读性。

      public class DataProcessor {
          public boolean processData(int value1, int value2, int value3) {
              return (value1 > 0 && value2 > 0) || (value1 < 0 && value3 > 0) && (value2 < 0 || value3 < 0);
          }
      }
      
  2. 多步骤计算解释

    • 给定以下 Java 代码,calculateFinalGrade 方法有多个步骤的计算。请引入解释性变量来清晰展示计算过程。
    public class GradeCalculator {
        public double calculateFinalGrade(double assignmentScore, double midtermScore, double finalExamScore) {
            return assignmentScore * 0.2 + midtermScore * 0.3 + finalExamScore * 0.5;
        }
    }
    

综合拓展练习题

  1. 复杂业务逻辑解释与代码审查模拟

    • 考虑一个简单的 Java 电商系统,有一个 Order 类,其中的 isValidOrder 方法包含复杂的业务逻辑。请引入解释性变量来重构该方法。
    • 假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
    import java.util.List;
    
    class Product {
        private double price;
        private boolean isInStock;
    
        public Product(double price, boolean isInStock) {
            this.price = price;
            this.isInStock = isInStock;
        }
    
        public double getPrice() {
            return price;
        }
    
        public boolean isInStock() {
            return isInStock;
        }
    }
    
    public class Order {
        private List<Product> products;
        private double totalAmount;
        private boolean isPaid;
    
        public Order(List<Product> products, double totalAmount, boolean isPaid) {
            this.products = products;
            this.totalAmount = totalAmount;
            this.isPaid = isPaid;
        }
    
        public boolean isValidOrder() {
            return products != null && !products.isEmpty() && totalAmount > 0 && isPaid && products.stream().allMatch(Product::isInStock);
        }
    }