简介
"用查询替换派生变量"是一种消除冗余状态管理的重构手法。通过将可通过其他数据计算得出的变量替换为即时计算的方法,可以简化代码结构并减少状态同步问题。
针对的症状(代码坏味道)
- 存在可通过其他字段计算得出的临时变量
- 需要手动维护的派生状态
- 多个变量之间存在计算依赖关系
用查询替换派生变量的详细步骤
- 识别派生变量
- 查找所有仅通过其他字段计算得出的变量
- 确认变量值没有外部修改入口
- 创建查询方法
- 将变量计算逻辑封装为无参数方法
- 确保方法没有副作用
- 替换变量引用
- 将变量访问改为方法调用
- 处理所有读写点
- 移除原变量
- 删除变量声明和初始化代码
- 清理关联的setter方法
示例
重构前代码:
class ShoppingCart {
private List<Item> items = new ArrayList<>();
private double total;
public void addItem(Item item) {
items.add(item);
total += item.getPrice();
}
public void removeItem(Item item) {
items.remove(item);
total -= item.getPrice();
}
public double getTotal() {
return total;
}
}
重构步骤:
-
创建总价计算方法:
public double calculateTotal() { return items.stream() .mapToDouble(Item::getPrice) .sum(); } -
替换total变量引用:
class ShoppingCart { private List<Item> items = new ArrayList<>(); public void addItem(Item item) { items.add(item); } public void removeItem(Item item) { items.remove(item); } public double getTotal() { return calculateTotal(); } private double calculateTotal() { return items.stream() .mapToDouble(Item::getPrice) .sum(); } }
练习
基础练习题
-
简单派生变量替换
// 重构前 class Rectangle { private int width; private int height; private int area; public Rectangle(int w, int h) { width = w; height = h; area = width * height; } public int getArea() { return area; } }
进阶练习题
-
动态计算替换
// 重构前 class Order { private List<Product> products; private double discount; private double total; public void applyDiscount(double rate) { discount = calculateSubtotal() * rate; total = calculateSubtotal() - discount; } private double calculateSubtotal() { return products.stream() .mapToDouble(Product::getPrice) .sum(); } }
综合拓展练习题
-
多重依赖变量替换
// 重构前 class Student { private List<Course> courses; private double gpa; private boolean isHonor; public void updateStatus() { gpa = calculateGPA(); isHonor = gpa >= 3.8; } private double calculateGPA() { return courses.stream() .mapToDouble(Course::getScore) .average() .orElse(0.0); } }
代码审查要点
-
优点:
- 消除状态同步问题
- 简化对象生命周期管理
- 提高代码可测试性
-
潜在问题:
- 避免高频调用的性能损耗
- 确保计算方法的幂等性
- 处理并发环境下的计算一致性