简介
“将语句移动到函数中”(Move Statements into Function)是一种重构手法,通过将一段代码移动到已有的函数中,可以减少代码重复并提高代码的可读性。这种方法特别适用于那些在多个地方重复出现的代码块。以下是进行“将语句移动到函数中”重构的详细步骤:
针对的症状(代码坏味道)
- 重复的代码(Duplicated Code)
- 过长的方法(Long Method)
- 代码块内的逻辑过于复杂(Complex Logic in a Block)
将语句移动到函数中(Move Statements into Function)的详细步骤
- 识别需要移动的代码块
- 寻找重复代码:在代码中找到重复出现的代码块。
- 识别复杂逻辑:找到逻辑复杂且难以理解的代码块。
- 评估代码块的独立性:确保移动的代码块具有一定的独立性和完整性,可以作为一个独立的方法存在。
- 选择目标函数
- 选择合适的目标函数:选择一个已经存在的函数,该函数的功能与需要移动的代码块相关。
- 确保目标函数的参数和返回值能够满足需求:确保目标函数能够接收必要的参数,并返回必要的值。
- 移动代码块
- 将代码块移动到目标函数中:将选定的代码块复制到目标函数中。
- 传递必要的参数:确保目标函数能够接收必要的参数,以完成其功能。
- 返回值:如果需要,确保目标函数能够返回必要的值。
- 替换原始代码
- 调用目标函数:在原始代码中,用调用目标函数的语句替换移动的代码块。
- 清理未使用的变量:删除不再需要的局部变量或临时变量。
- 测试
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 手动测试:如果有必要,进行手动测试以验证功能的正确性。
- 代码审查
- 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
- 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明移动语句的影响。
示例
假设有一个方法 processOrder,其中包含一段重复的计算逻辑,我们希望将其移动到已有的 calculateTotal 方法中:
public class Order {
private double price;
private int quantity;
private double taxRate;
private double shippingCost;
public double calculateTotal() {
double subTotal = price * quantity;
double tax = subTotal * taxRate;
double finalTotal = subTotal + tax;
if (quantity > 10) {
finalTotal += shippingCost;
}
return finalTotal;
}
public void processOrder() {
double subTotal = price * quantity;
double tax = subTotal * taxRate;
double finalTotal = subTotal + tax;
if (quantity > 10) {
finalTotal += shippingCost;
}
System.out.println("Order total: " + finalTotal);
}
}
步骤如下:
- 识别需要移动的代码块:
- 重复的计算逻辑:
double subTotal = price * quantity; double tax = subTotal * taxRate; double finalTotal = subTotal + tax; if (quantity > 10) { finalTotal += shippingCost; }
- 重复的计算逻辑:
- 选择目标函数:
- 选择
calculateTotal方法作为目标函数。
- 选择
- 移动代码块:
- 将重复的计算逻辑移动到
calculateTotal方法中。
- 将重复的计算逻辑移动到
- 替换原始代码:
-
在
processOrder方法中,用调用calculateTotal方法的语句替换移动的代码块:public class Order { private double price; private int quantity; private double taxRate; private double shippingCost; public double calculateTotal() { double subTotal = price * quantity; double tax = subTotal * taxRate; double finalTotal = subTotal + tax; if (quantity > 10) { finalTotal += shippingCost; } return finalTotal; } public void processOrder() { double finalTotal = calculateTotal(); System.out.println("Order total: " + finalTotal); } }
-
- 测试:
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 代码审查:
- 让同事审查代码,确保没有引入新的问题。
练习
基础练习题
- 重复代码的移动
-
给定以下 Java 代码,
processUserInput方法和validateUserInput方法都包含重复的代码块。请将重复的代码块移动到validateInput方法中。public class UserProcessor { private String userInput; public UserProcessor(String userInput) { this.userInput = userInput; } public void processUserInput() { if (validateInput(userInput)) { System.out.println("Processing user input: " + userInput); } else { System.out.println("Invalid user input."); } } public void validateUserInput() { if (validateInput(userInput)) { System.out.println("User input is valid."); } else { System.out.println("User input is invalid."); } } private boolean validateInput(String input) { return input != null && !input.isEmpty(); } }
-
- 复杂逻辑块的移动
-
下面的 Java 代码中,
calculateRectangleArea和calculateSquareArea方法都包含复杂的计算逻辑。请将计算逻辑移动到calculateArea方法中。public class ShapeCalculator { public void calculateRectangleArea(int length, int width) { int area = calculateArea(length, width); System.out.println("Rectangle area is: " + area); } public void calculateSquareArea(int side) { int area = calculateArea(side, side); System.out.println("Square area is: " + area); } private int calculateArea(int dim1, int dim2) { return dim1 * dim2; } }
-
进阶练习题
- 复杂逻辑块移动与参数传递
-
在这段 Java 代码中,
calculateShippingCost方法内的逻辑较为复杂。请将计算折扣后的运费逻辑移动到calculateDiscountedShippingCost方法中,并正确处理参数传递。public class OrderShipping { private double baseShippingCost; private int orderQuantity; private double discountRate; public OrderShipping(double baseShippingCost, int orderQuantity, double discountRate) { this.baseShippingCost = baseShippingCost; this.orderQuantity = orderQuantity; this.discountRate = discountRate; } public double calculateShippingCost() { return calculateDiscountedShippingCost(baseShippingCost, orderQuantity, discountRate); } private double calculateDiscountedShippingCost(double baseCost, int quantity, double rate) { double discountAmount = baseCost * rate; double discountedShippingCost = baseCost - discountAmount; if (quantity > 5) { discountedShippingCost -= 5; } return discountedShippingCost; } }
-
- 移动语句与返回值处理
-
给定以下 Java 代码,
processData方法包含复杂逻辑。请将数据转换和计算结果的部分移动到transformData和calculateSum方法中,并确保返回值正确处理。import java.util.ArrayList; import java.util.List; public class DataProcessor { public int processData(int[] dataArray) { List<Integer> processedData = transformData(dataArray); return calculateSum(processedData); } private List<Integer> transformData(int[] dataArray) { List<Integer> processedData = new ArrayList<>(); for (int num : dataArray) { num = num * 2; if (num > 10) { num = num - 5; } processedData.add(num); } return processedData; } private int calculateSum(List<Integer> processedData) { int sum = 0; for (int num : processedData) { sum += num; } return sum; } }
-
综合拓展练习题
- 多模块移动与代码审查模拟
-
考虑一个简单的 Java 电商系统,有
Product类、Cart类和Order类。Cart类中的calculateCartTotal方法和Order类中的calculateOrderTotal方法都有重复且复杂的计算总价逻辑,同时Cart类中的applyCartDiscount方法逻辑也较为复杂。 -
请对这些方法进行 “将语句移动到函数中” 重构,将重复和复杂逻辑移动到独立方法中。
-
假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
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 = calculateSubtotal(products); return applyDiscount(total, discountRate); } public void applyCartDiscount() { if (products.length > 3) { discountRate = adjustDiscountRate(discountRate, 0.05); } if (calculateCartTotal() > 100) { discountRate = adjustDiscountRate(discountRate, 0.1); } } private double calculateSubtotal(Product[] products) { double total = 0; for (Product product : products) { total += product.getPrice(); } return total; } private double applyDiscount(double total, double rate) { double discountAmount = total * rate; return total - discountAmount; } private double adjustDiscountRate(double rate, double increment) { return rate + increment; } } 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 = calculateSubtotal(products); return total + shippingCost; } private double calculateSubtotal(Product[] products) { double total = 0; for (Product product : products) { total += product.getPrice(); } return total; } }
-