简介
“移动方法”(Move Method)是一种常用的重构手法,通过将一个方法从一个类移动到另一个类中,可以提高代码的组织性和可维护性。这种方法特别适用于当一个方法与其所在类的职责不匹配,或者与另一个类的职责更相关时。以下是进行“移动方法”重构的详细步骤:
针对的症状(代码坏味道)
- 方法与其所在类的职责不匹配(Feature Envy)
- 方法频繁访问另一个类的数据(Inappropriate Intimacy)
- 类之间的职责分配不合理(Poor Class Responsibility Assignment)
移动方法(Move Method)的详细步骤
- 识别需要移动的方法
- 寻找职责不匹配的方法:在代码中找到与其所在类的职责不匹配的方法。
- 识别频繁访问其他类数据的方法:找到频繁访问另一个类的数据的方法。
- 评估方法的独立性:确保移动的方法具有一定的独立性和完整性,可以作为一个独立的方法存在。
- 选择目标类
- 选择合适的目标类:选择一个与移动方法职责更相关的类作为目标类。
- 确保目标类的可访问性:确保目标类能够访问移动方法所需的数据和方法。
- 移动方法
- 复制方法到目标类:将选定的方法复制到目标类中。
- 调整方法签名:根据目标类的上下文调整方法的签名,包括参数和返回值。
- 传递必要的参数:确保目标类的方法能够接收必要的参数,以完成其功能。
- 返回值:如果需要,确保目标类的方法能够返回必要的值。
- 替换原始代码
- 调用新方法:在原始类中,用调用新方法的语句替换原始方法的调用。
- 清理未使用的代码:删除原始类中不再需要的方法或代码。
- 测试
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 手动测试:如果有必要,进行手动测试以验证功能的正确性。
- 代码审查
- 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
- 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明移动方法的影响。
示例
假设有一个类 Order,其中包含一个方法 calculateShippingCost,该方法与 Shipping 类的职责更相关,我们希望对其进行“移动方法”的重构:
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 += calculateShippingCost();
}
return finalTotal;
}
private double calculateShippingCost() {
return shippingCost * quantity;
}
}
public class Shipping {
private double shippingCost;
public Shipping(double shippingCost) {
this.shippingCost = shippingCost;
}
public double calculateShippingCost(int quantity) {
return shippingCost * quantity;
}
}
步骤如下:
- 识别需要移动的方法:
- 职责不匹配的方法:
calculateShippingCost方法与Order类的职责不匹配,与Shipping类的职责更相关。
- 职责不匹配的方法:
- 选择目标类:
- 选择合适的目标类:
Shipping类。
- 选择合适的目标类:
- 移动方法:
- 复制方法到目标类:将
calculateShippingCost方法复制到Shipping类中。 - 调整方法签名:根据
Shipping类的上下文调整方法的签名,包括参数和返回值。 - 传递必要的参数:确保
Shipping类的方法能够接收必要的参数,以完成其功能。 - 返回值:确保
Shipping类的方法能够返回必要的值。
- 复制方法到目标类:将
- 替换原始代码:
-
在
Order类中,用调用新方法的语句替换原始方法的调用:public class Order { private double price; private int quantity; private double taxRate; private Shipping shipping; public Order(double price, int quantity, double taxRate, Shipping shipping) { this.price = price; this.quantity = quantity; this.taxRate = taxRate; this.shipping = shipping; } public double calculateTotal() { double subTotal = price * quantity; double tax = subTotal * taxRate; double finalTotal = subTotal + tax; if (quantity > 10) { finalTotal += shipping.calculateShippingCost(quantity); } return finalTotal; } }
-
- 测试:
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 代码审查:
- 让同事审查代码,确保没有引入新的问题。
练习
基础练习题
- 移动方法与职责匹配
-
给定以下 Java 代码,
Customer类中的calculateDiscount方法与Customer类的职责不匹配,与Discount类的职责更相关。请将calculateDiscount方法移动到Discount类中。public class Customer { private String name; private double totalPurchases; private Discount discount; public Customer(String name, double totalPurchases, Discount discount) { this.name = name; this.totalPurchases = totalPurchases; this.discount = discount; } public double calculateTotal() { return totalPurchases - discount.calculateDiscount(totalPurchases); } } public class Discount { private double discountRate; public Discount(double discountRate) { this.discountRate = discountRate; } public double calculateDiscount(double totalPurchases) { return totalPurchases * discountRate; } }
-
- 移动方法与参数传递
-
下面的 Java 代码中,
Order类中的calculateTax方法与Order类的职责不匹配,与Tax类的职责更相关。请将calculateTax方法移动到Tax类中,并正确处理参数传递。public class Order { private double price; private int quantity; private Tax tax; public Order(double price, int quantity, Tax tax) { this.price = price; this.quantity = quantity; this.tax = tax; } public double calculateTotal() { return price * quantity + tax.calculateTax(price * quantity); } } public class Tax { private double taxRate; public Tax(double taxRate) { this.taxRate = taxRate; } public double calculateTax(double amount) { return amount * taxRate; } }
-
进阶练习题
- 复杂逻辑块移动与参数传递
-
在这段 Java 代码中,
Order类中的calculateShippingCost方法与Order类的职责不匹配,与Shipping类的职责更相关。请将calculateShippingCost方法移动到Shipping类中,并正确处理参数传递。public class Order { private double price; private int quantity; private Shipping shipping; public Order(double price, int quantity, Shipping shipping) { this.price = price; this.quantity = quantity; this.shipping = shipping; } public double calculateTotal() { return price * quantity + shipping.calculateShippingCost(quantity); } } public class Shipping { private double baseShippingCost; private double discountRate; public Shipping(double baseShippingCost, double discountRate) { this.baseShippingCost = baseShippingCost; this.discountRate = discountRate; } public double calculateShippingCost(int quantity) { double shippingCost = baseShippingCost * quantity; return shippingCost - (shippingCost * discountRate); } }
-
- 移动方法与返回值处理
-
给定以下 Java 代码,
Order类中的calculateTotal方法包含复杂逻辑。请将计算总价的部分提取到OrderCalculator类中,并确保返回值正确处理。public class Order { private double price; private int quantity; private double taxRate; private double shippingCost; private OrderCalculator orderCalculator; public Order(double price, int quantity, double taxRate, double shippingCost, OrderCalculator orderCalculator) { this.price = price; this.quantity = quantity; this.taxRate = taxRate; this.shippingCost = shippingCost; this.orderCalculator = orderCalculator; } public double calculateTotal() { return orderCalculator.calculateTotal(price, quantity, taxRate, shippingCost); } } public class OrderCalculator { public double calculateTotal(double price, int quantity, double taxRate, double shippingCost) { double subTotal = price * quantity; double tax = subTotal * taxRate; double finalTotal = subTotal + tax; if (quantity > 10) { finalTotal += shippingCost; } return finalTotal; } }
-
综合拓展练习题
- 多模块移动与代码审查模拟
- 考虑一个简单的 Java 电商系统,有
Product类、Cart类和Order类。Cart类中的calculateCartTotal方法和Order类中的calculateOrderTotal方法都有重复且复杂的计算总价逻辑,同时Cart类中的applyCartDiscount方法逻辑也较为复杂。 - 请对这些方法进行 “移动方法” 重构,将重复和复杂逻辑移动到
OrderCalculator类中。 - 假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
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; private OrderCalculator orderCalculator; public Cart(Product[] products, double discountRate, OrderCalculator orderCalculator) { this.products = products; this.discountRate = discountRate; this.orderCalculator = orderCalculator; } public double calculateCartTotal() { return orderCalculator.calculateCartTotal(products, discountRate); } public void applyCartDiscount() { if (products.length > 3) { discountRate = orderCalculator.adjustDiscountRate(discountRate, 0.05); } if (calculateCartTotal() > 100) { discountRate = orderCalculator.adjustDiscountRate(discountRate, 0.1); } } } class Order { private Product[] products; private double shippingCost; private OrderCalculator orderCalculator; public Order(Product[] products, double shippingCost, OrderCalculator orderCalculator) { this.products = products; this.shippingCost = shippingCost; this.orderCalculator = orderCalculator; } public double calculateOrderTotal() { return orderCalculator.calculateOrderTotal(products, shippingCost); } } class OrderCalculator { public double calculateCartTotal(Product[] products, double discountRate) { double total = calculateSubtotal(products); return applyDiscount(total, discountRate); } public double calculateOrderTotal(Product[] products, double shippingCost) { double total = calculateSubtotal(products); return total + shippingCost; } 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; } public double adjustDiscountRate(double rate, double increment) { return rate + increment; } }
- 考虑一个简单的 Java 电商系统,有