重构手法——数据重组类 | 数据关系重构 | 移动字段

67 阅读5分钟

简介

“移动字段”(Move Field)是一种重构手法,通过将一个字段从一个类移动到另一个类,从而更好地组织代码结构,使类的职责更加清晰。这种方法特别适用于当一个字段被另一个类频繁使用时,或者当一个字段与当前类的职责不匹配时。以下是进行“移动字段”重构的详细步骤:

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

  • 字段被其他类频繁使用(Field Used by Another Class)
  • 字段与当前类的职责不匹配(Field Doesn't Belong to Current Class)

移动字段(Move Field)的详细步骤

  1. 识别需要移动的字段
    • 寻找被其他类频繁使用的字段。
    • 确定这些字段是否更适合放在另一个类中。
  2. 创建目标类中的字段
    • 在目标类中创建相同的字段。
    • 确保目标类有适当的访问方法(getter 和 setter)。
  3. 更新原始类中的字段引用
    • 将原始类中对字段的引用替换为对目标类中字段的引用。
    • 确保所有相关的地方都更新为使用目标类中的字段。
  4. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
    • 手动测试:如果有必要,进行手动测试以验证功能的正确性。
  5. 代码审查
    • 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
    • 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明移动字段的影响。

示例

假设有一个类 Order,其中包含一个字段 customerName,但这个字段被 Customer 类频繁使用,我们希望对其进行“移动字段”的重构:

public class Order {
   private String customerName;
   private double totalAmount;

   public Order(String customerName, double totalAmount) {
      this.customerName = customerName;
      this.totalAmount = totalAmount;
   }

   public String getCustomerName() {
      return customerName;
   }

   public double getTotalAmount() {
      return totalAmount;
   }
}

public class Customer {
   private String name;

   public Customer(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}

步骤如下:

  1. 识别需要移动的字段:

    • 字段 customerNameCustomer 类频繁使用。
  2. 创建目标类中的字段:

    • Customer 类中创建字段 name

      public class Customer {
         private String name;
      
         public Customer(String name) {
            this.name = name;
         }
      
         public String getName() {
            return name;
         }
      }
      
  3. 更新原始类中的字段引用:

    • Order 类中对 customerName 的引用替换为对 Customer 类中 name 的引用:

      public class Order {
         private Customer customer;
         private double totalAmount;
      
         public Order(Customer customer, double totalAmount) {
            this.customer = customer;
            this.totalAmount = totalAmount;
         }
      
         public String getCustomerName() {
            return customer.getName();
         }
      
         public double getTotalAmount() {
            return totalAmount;
         }
      }
      
  4. 测试:

    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
  5. 代码审查:

    • 让同事审查代码,确保没有引入新的问题。

练习

基础练习题

  1. 移动简单字段
    • 给定以下 Java 代码,Employee 类中的 departmentName 字段被 Department 类频繁使用。请将该字段移动到 Department 类中。

      public class Employee {
         private String name;
         private String departmentName;
      
         public Employee(String name, String departmentName) {
            this.name = name;
            this.departmentName = departmentName;
         }
      
         public String getName() {
            return name;
         }
      
         public String getDepartmentName() {
            return departmentName;
         }
      }
      
      public class Department {
         private String name;
      
         public Department(String name) {
            this.name = name;
         }
      
         public String getName() {
            return name;
         }
      }
      
  2. 移动复杂字段
    • 下面的 Java 代码中有两个类 ProductCategoryProduct 类中的 categoryName 字段被 Category 类频繁使用。请将该字段移动到 Category 类中。

      public class Product {
         private String name;
         private String categoryName;
      
         public Product(String name, String categoryName) {
            this.name = name;
            this.categoryName = categoryName;
         }
      
         public String getName() {
            return name;
         }
      
         public String getCategoryName() {
            return categoryName;
         }
      }
      
      public class Category {
         private String name;
      
         public Category(String name) {
            this.name = name;
         }
      
         public String getName() {
            return name;
         }
      }
      

进阶练习题

  1. 移动复杂逻辑字段
    • 在这段 Java 代码中,Order 类中的 shippingCost 字段被 ShippingInfo 类频繁使用。请将该字段移动到 ShippingInfo 类中。

      public class Order {
         private double totalAmount;
         private double shippingCost;
      
         public Order(double totalAmount, double shippingCost) {
            this.totalAmount = totalAmount;
            this.shippingCost = shippingCost;
         }
      
         public double getTotalAmount() {
            return totalAmount;
         }
      
         public double getShippingCost() {
            return shippingCost;
         }
      }
      
      public class ShippingInfo {
         private double baseShippingCost;
         private double discountRate;
      
         public ShippingInfo(double baseShippingCost, double discountRate) {
            this.baseShippingCost = baseShippingCost;
            this.discountRate = discountRate;
         }
      
         public double getBaseShippingCost() {
            return baseShippingCost;
         }
      
         public double getDiscountRate() {
            return discountRate;
         }
      }
      
  2. 移动方法与返回值字段
    • 给定以下 Java 代码,DataProcessor 类中的 dataArray 字段被 DataTransformer 类频繁使用。请将该字段移动到 DataTransformer 类中。

      import java.util.ArrayList;
      import java.util.List;
      
      public class DataProcessor {
         private int[] dataArray;
      
         public DataProcessor(int[] dataArray) {
            this.dataArray = dataArray;
         }
      
         public int[] getDataArray() {
            return dataArray;
         }
      
         public int processData() {
            List<Integer> processedData = new ArrayList<>();
            for (int num : dataArray) {
               num = num * 2;
               if (num > 10) {
                  num = num - 5;
               }
               processedData.add(num);
            }
            int sum = 0;
            for (int num : processedData) {
               sum += num;
            }
            return sum;
         }
      }
      
      public class DataTransformer {
         private int[] dataArray;
      
         public DataTransformer(int[] dataArray) {
            this.dataArray = dataArray;
         }
      
         public int[] getDataArray() {
            return dataArray;
         }
      }
      

综合拓展练习题

  1. 多模块移动字段与代码审查模拟
    • 考虑一个简单的 Java 电商系统,有 Product 类、Cart 类和 Order 类。Cart 类中的 discountRate 字段被 Order 类频繁使用,同时 Order 类中的 shippingCost 字段被 ShippingInfo 类频繁使用。

    • 请对这些字段进行 “移动字段” 重构,将字段移动到更适合的类中。

    • 假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。

      class Product {
         private double price;
      
         public Product(double price) {
            this.price = price;
         }
      
         public double getPrice() {
            return price;
         }
      }
      
      class Cart {
         private Product[] products;
         private double discountRate;
      
         public Cart(Product[] products, double discountRate) {
            this.products = products;
            this.discountRate = discountRate;
         }
      
         public double calculateCartTotal() {
            double total = 0;
            for (Product product : products) {
               total += product.getPrice();
            }
            return total - (total * discountRate);
         }
      
         public void applyCartDiscount() {
            if (products.length > 3) {
               discountRate += 0.05;
            }
            if (calculateCartTotal() > 100) {
               discountRate += 0.1;
            }
         }
      }
      
      class Order {
         private Product[] products;
         private double shippingCost;
      
         public Order(Product[] products, double shippingCost) {
            this.products = products;
            this.shippingCost = shippingCost;
         }
      
         public double calculateOrderTotal() {
            double total = 0;
            for (Product product : products) {
               total += product.getPrice();
            }
            return total + shippingCost;
         }
      }
      
      class ShippingInfo {
         private double baseShippingCost;
         private double discountRate;
      
         public ShippingInfo(double baseShippingCost, double discountRate) {
            this.baseShippingCost = baseShippingCost;
            this.discountRate = discountRate;
         }
      
         public double getBaseShippingCost() {
            return baseShippingCost;
         }
      
         public double getDiscountRate() {
            return discountRate;
         }
      }