重构手法——函数行为重塑类 | 函数签名 | 更改签名

127 阅读13分钟

简介

“Change Function Declaration - 更改函数声明 | Change Signature - 更改签名”是一种重构手法,用于修改函数或方法的签名。函数或方法的签名包括函数名称、参数列表(参数的数量、类型和顺序)以及返回值类型。使用该技术可以让函数或方法更符合新的需求,提高代码的可维护性和可扩展性,同时保持其原有功能不变。

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

  • 函数或方法参数不合理:当函数或方法的参数列表不符合当前使用场景,例如参数顺序混乱、参数过多或参数类型不合适时,可使用该重构技术。
  • 函数或方法命名不清晰:当函数或方法的名称不能准确描述其功能时,可能需要同时修改函数名称和参数列表。
  • 函数或方法的返回值不满足需求:当函数或方法的返回值类型或内容需要调整,以更好地满足调用者的需求时。

Change Signature 的详细步骤

  1. 分析现有签名
    • 检查函数或方法的名称:确定函数或方法名称是否准确反映其功能,如果不准确,考虑修改。
    • 检查参数列表:分析参数的数量、类型、顺序以及每个参数的使用情况,判断是否需要调整。
    • 检查返回值:评估返回值的类型和内容是否符合调用者的期望和使用场景。
  2. 规划新的签名
    • 确定新的函数或方法名称(如果需要):选择一个更具描述性的名称,能清晰表达函数或方法的功能。
    • 规划新的参数列表:确定所需的参数,包括数量、类型和顺序,删除不必要的参数,添加需要的参数。
    • 确定新的返回值(如果需要):根据调用者的需求,考虑修改返回值的类型或内容。
  3. 修改函数或方法的声明
    • 更新函数或方法的名称(如果有更改):在函数或方法的声明处修改其名称。
    • 修改参数列表:根据规划的新参数列表,添加、删除或调整参数。
    • 更新返回值类型(如果有更改):在函数或方法的声明处修改返回值类型。
  4. 调整函数或方法的内部实现
    • 根据新的参数使用情况:在函数或方法内部,更新对参数的使用逻辑,确保正确使用新参数。
    • 调整返回值逻辑(如果有更改):修改函数或方法内部逻辑,以提供正确的返回值。
  5. 更新函数或方法的调用处
    • 找到所有调用该函数或方法的地方:在整个代码库中找出调用该函数或方法的代码位置。
    • 修改调用处的参数:根据新的参数列表,在调用处传递正确的参数。
    • 处理返回值(如果有更改):根据新的返回值类型,在调用处正确处理返回值。
  6. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行现有的单元测试,确保修改签名的操作没有引入新的错误。如果没有单元测试,编写相应的测试用例进行测试。
    • 手动测试:对于涉及用户界面或外部系统交互的函数,进行手动测试,确保功能的正确性。
  7. 代码审查
    • 同行评审:请同事或其他团队成员审查代码,确保修改签名的操作符合代码质量和可维护性标准。
    • 文档更新:如果有相关的文档,更新文档中关于该函数或方法的描述,包括新的签名及其作用。

示例

假设我们有一个简单的 Java 类,其中包含一个 printMessage 方法,它接收一个字符串作为参数并打印该消息,但现在我们需要修改其签名。

public class MessagePrinter {
    public void printMessage(String message) {
        System.out.println(message);
    }
}

以下是使用“Change Signature”技术的步骤:

  1. 分析现有签名:

    • 函数名称 printMessage 比较合适,但假设我们需要添加一个参数来控制消息的打印次数。
    • 当前只有一个参数 message,需要添加一个新的 int 类型参数表示打印次数。
    • 无返回值,无需修改。
  2. 规划新的签名:

    • 函数名称保持不变。
    • 新的参数列表:(String message, int printCount)
    • 无返回值。
  3. 修改函数或方法的声明:

    • printMessage 方法的声明处添加新的参数。

      public class MessagePrinter {
          public void printMessage(String message, int printCount) {
      // 函数内部逻辑将根据新的参数进行修改
          }
      }
      
  4. 调整函数或方法的内部实现:

    • 根据新的参数 printCount,修改函数内部逻辑,多次打印消息。

      public class MessagePrinter {
          public void printMessage(String message, int printCount) {
              for (int i = 0; i < printCount; i++) {
                  System.out.println(message);
              }
          }
      }
      
  5. 更新函数或方法的调用处:

    • 在调用 printMessage 的地方添加新的参数。

      public class Main {
          public static void main(String[] args) {
              MessagePrinter printer = new MessagePrinter();
              printer.printMessage("Hello, World!", 3);
          }
      }
      
  6. 测试:

    • 编译代码,确保代码编译通过。
    • 运行测试,确保消息能正确打印指定次数。
    • 进行手动测试,检查不同打印次数的情况。
  7. 代码审查:

    • 请同事检查代码,确保代码可读性和可维护性没有下降。
    • 更新文档,说明 printMessage 方法新增了 printCount 参数,以及其对方法行为的影响。

练习

基础练习题

  1. 简单签名修改

    • 对于以下 Java 代码,将 calculateSum 方法的参数列表修改为接收一个 int[] 类型的数组,并计算数组元素的总和。

      public class SumCalculator {
          public int calculateSum(int a, int b) {
              return a + b;
          }
      }
      
  2. 修改方法名称和参数

    • 对于以下 Java 代码,将 validateUser 方法的名称修改为 checkUserAge,并将参数修改为接收 User 对象,根据 User 对象的年龄进行验证。

      public class UserValidator {
          public boolean validateUser(int age) {
              return age >= 18;
          }
      }
      

进阶练习题

  1. 复杂签名修改

    • 在以下 Java 代码中,将 processData 方法的签名修改为接收 List<Integer> 类型的列表作为输入,添加一个 String 类型的参数 operation 表示操作类型(如 "sum"、"average"),并根据操作类型修改函数内部逻辑。

      import java.util.ArrayList;
      import java.util.List;
      
      public class DataProcessor {
          public int processData(List<Integer> dataList) {
              int sum = 0;
              for (int num : dataList) {
                  sum += num;
              }
              return sum;
          }
      }
      
  2. 签名修改与返回值调整

    • 对于以下 Java 代码,将 calculatePrice 方法的签名修改为接收 Product 对象作为参数,同时修改返回值为 double 类型,并根据 Product 的属性计算价格。
    public class PriceCalculator {
        public int calculatePrice(int quantity) {
            return quantity * 10;
        }
    }
    

综合拓展练习题

  1. 多模块签名修改

    • 考虑一个 Java 订单系统,包含 OrderServiceProductService 类。在 OrderService 类的 placeOrder 方法中修改签名,将参数 Product 改为 Product[] 类型的数组,添加一个 String 类型的参数 customerName,并修改返回值为 Order 对象。在 ProductService 类的 getProduct 方法中修改签名,添加一个 int 类型的参数 version,并修改返回值为 Product 对象。

        public class OrderService {
          public void placeOrder(Product product, int quantity) {
              System.out.println("Placing order for " + quantity + " of " + product.getName());
          }
      }
      
      public class Product {
          private String name;
      
          public Product(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      }
      
      public class ProductService {
          public Product getProduct(int productId) {
              System.out.println("Getting product with id: " + productId);
              return new Product("Sample Product");
          }
      }
      
  2. 代码审查模拟

    • 完成上述多模块签名修改的操作后,模拟一份代码审查报告,指出修改签名后的优点和可能存在的潜在问题.## 简介

“Change Function Declaration - 更改函数声明 | Change Signature - 更改签名”是一种重构手法,用于修改函数或方法的签名。函数或方法的签名包括函数名称、参数列表(参数的数量、类型和顺序)以及返回值类型。使用该技术可以让函数或方法更符合新的需求,提高代码的可维护性和可扩展性,同时保持其原有功能不变。

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

  • 函数或方法参数不合理:当函数或方法的参数列表不符合当前使用场景,例如参数顺序混乱、参数过多或参数类型不合适时,可使用该重构技术。
  • 函数或方法命名不清晰:当函数或方法的名称不能准确描述其功能时,可能需要同时修改函数名称和参数列表。
  • 函数或方法的返回值不满足需求:当函数或方法的返回值类型或内容需要调整,以更好地满足调用者的需求时。

Change Signature 的详细步骤

  1. 分析现有签名
    • 检查函数或方法的名称:确定函数或方法名称是否准确反映其功能,如果不准确,考虑修改。
    • 检查参数列表:分析参数的数量、类型、顺序以及每个参数的使用情况,判断是否需要调整。
    • 检查返回值:评估返回值的类型和内容是否符合调用者的期望和使用场景。
  2. 规划新的签名
    • 确定新的函数或方法名称(如果需要):选择一个更具描述性的名称,能清晰表达函数或方法的功能。
    • 规划新的参数列表:确定所需的参数,包括数量、类型和顺序,删除不必要的参数,添加需要的参数。
    • 确定新的返回值(如果需要):根据调用者的需求,考虑修改返回值的类型或内容。
  3. 修改函数或方法的声明
    • 更新函数或方法的名称(如果有更改):在函数或方法的声明处修改其名称。
    • 修改参数列表:根据规划的新参数列表,添加、删除或调整参数。
    • 更新返回值类型(如果有更改):在函数或方法的声明处修改返回值类型。
  4. 调整函数或方法的内部实现
    • 根据新的参数使用情况:在函数或方法内部,更新对参数的使用逻辑,确保正确使用新参数。
    • 调整返回值逻辑(如果有更改):修改函数或方法内部逻辑,以提供正确的返回值。
  5. 更新函数或方法的调用处
    • 找到所有调用该函数或方法的地方:在整个代码库中找出调用该函数或方法的代码位置。
    • 修改调用处的参数:根据新的参数列表,在调用处传递正确的参数。
    • 处理返回值(如果有更改):根据新的返回值类型,在调用处正确处理返回值。
  6. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行现有的单元测试,确保修改签名的操作没有引入新的错误。如果没有单元测试,编写相应的测试用例进行测试。
    • 手动测试:对于涉及用户界面或外部系统交互的函数,进行手动测试,确保功能的正确性。
  7. 代码审查
    • 同行评审:请同事或其他团队成员审查代码,确保修改签名的操作符合代码质量和可维护性标准。
    • 文档更新:如果有相关的文档,更新文档中关于该函数或方法的描述,包括新的签名及其作用。

示例

假设我们有一个简单的 Java 类,其中包含一个 printMessage 方法,它接收一个字符串作为参数并打印该消息,但现在我们需要修改其签名。

public class MessagePrinter {
    public void printMessage(String message) {
        System.out.println(message);
    }
}

以下是使用“Change Signature”技术的步骤:

  1. 分析现有签名:

    • 函数名称 printMessage 比较合适,但假设我们需要添加一个参数来控制消息的打印次数。
    • 当前只有一个参数 message,需要添加一个新的 int 类型参数表示打印次数。
    • 无返回值,无需修改。
  2. 规划新的签名:

    • 函数名称保持不变。
    • 新的参数列表:(String message, int printCount)
    • 无返回值。
  3. 修改函数或方法的声明:

    • printMessage 方法的声明处添加新的参数。

      public class MessagePrinter {
          public void printMessage(String message, int printCount) {
      // 函数内部逻辑将根据新的参数进行修改
          }
      }
      
  4. 调整函数或方法的内部实现:

    • 根据新的参数 printCount,修改函数内部逻辑,多次打印消息。

      public class MessagePrinter {
          public void printMessage(String message, int printCount) {
              for (int i = 0; i < printCount; i++) {
                  System.out.println(message);
              }
          }
      }
      
  5. 更新函数或方法的调用处:

    • 在调用 printMessage 的地方添加新的参数。

      public class Main {
          public static void main(String[] args) {
              MessagePrinter printer = new MessagePrinter();
              printer.printMessage("Hello, World!", 3);
          }
      }
      
  6. 测试:

    • 编译代码,确保代码编译通过。
    • 运行测试,确保消息能正确打印指定次数。
    • 进行手动测试,检查不同打印次数的情况。
  7. 代码审查:

    • 请同事检查代码,确保代码可读性和可维护性没有下降。
    • 更新文档,说明 printMessage 方法新增了 printCount 参数,以及其对方法行为的影响。

练习

基础练习题

  1. 简单签名修改

    • 对于以下 Java 代码,将 calculateSum 方法的参数列表修改为接收一个 int[] 类型的数组,并计算数组元素的总和。

      public class SumCalculator {
          public int calculateSum(int a, int b) {
              return a + b;
          }
      }
      
  2. 修改方法名称和参数

    • 对于以下 Java 代码,将 validateUser 方法的名称修改为 checkUserAge,并将参数修改为接收 User 对象,根据 User 对象的年龄进行验证。

      public class UserValidator {
          public boolean validateUser(int age) {
              return age >= 18;
          }
      }
      

进阶练习题

  1. 复杂签名修改

    • 在以下 Java 代码中,将 processData 方法的签名修改为接收 List<Integer> 类型的列表作为输入,添加一个 String 类型的参数 operation 表示操作类型(如 "sum"、"average"),并根据操作类型修改函数内部逻辑。

      import java.util.ArrayList;
      import java.util.List;
      
      public class DataProcessor {
          public int processData(List<Integer> dataList) {
              int sum = 0;
              for (int num : dataList) {
                  sum += num;
              }
              return sum;
          }
      }
      
  2. 签名修改与返回值调整

    • 对于以下 Java 代码,将 calculatePrice 方法的签名修改为接收 Product 对象作为参数,同时修改返回值为 double 类型,并根据 Product 的属性计算价格。
    public class PriceCalculator {
        public int calculatePrice(int quantity) {
            return quantity * 10;
        }
    }
    

综合拓展练习题

  1. 多模块签名修改

    • 考虑一个 Java 订单系统,包含 OrderServiceProductService 类。在 OrderService 类的 placeOrder 方法中修改签名,将参数 Product 改为 Product[] 类型的数组,添加一个 String 类型的参数 customerName,并修改返回值为 Order 对象。在 ProductService 类的 getProduct 方法中修改签名,添加一个 int 类型的参数 version,并修改返回值为 Product 对象。

        public class OrderService {
          public void placeOrder(Product product, int quantity) {
              System.out.println("Placing order for " + quantity + " of " + product.getName());
          }
      }
      
      public class Product {
          private String name;
      
          public Product(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      }
      
      public class ProductService {
          public Product getProduct(int productId) {
              System.out.println("Getting product with id: " + productId);
              return new Product("Sample Product");
          }
      }
      
  2. 代码审查模拟

    • 完成上述多模块签名修改的操作后,模拟一份代码审查报告,指出修改签名后的优点和可能存在的潜在问题.