重构手法——代码健壮性增强类 | 防御性编程 | 引入断言

简介

"引入断言"(Introduce Assertion)是一种重构手法,通过在代码中添加断言来明确表达某些假设或条件,从而提高代码的可读性和可维护性。断言通常用于验证程序在某个特定点的状态是否符合预期,如果不符合,程序将抛出异常或终止执行。以下是进行"引入断言"重构的详细步骤:

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

  • 隐含的假设(Implicit Assumptions):代码中存在一些未明确表达的假设,可能导致潜在的错误。
  • 缺乏防御性编程(Lack of Defensive Programming):代码中没有对输入或状态进行充分的验证。

引入断言(Introduce Assertion)的详细步骤

  1. 识别需要引入断言的地方
    • 寻找代码中隐含的假设或条件。
    • 确定哪些条件在程序执行过程中必须为真。
  2. 添加断言
    • 使用断言语句明确表达这些条件。
    • 确保断言的条件清晰且易于理解。
  3. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保断言没有引入新的错误。
    • 手动测试:如果有必要,进行手动测试以验证断言的正确性。
  4. 代码审查
    • 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
    • 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明引入断言的影响。

示例

假设有一个方法 calculateTotal,其中包含一个隐含的假设,我们希望对其进行"引入断言"的重构:

java 复制代码
public class Order {
    private double price;
    private int quantity;
    private double taxRate;

    public double calculateTotal() {
        return price * quantity * (1 + taxRate);
    }
}

步骤如下:

  1. 识别需要引入断言的地方:

    • 隐含的假设:pricequantity 必须为正数,taxRate 必须为非负数。
  2. 添加断言:

    • 使用断言语句明确表达这些条件:

      java 复制代码
        public class Order {
          private double price;
          private int quantity;
          private double taxRate;
      
          public double calculateTotal() {
              assert price > 0 : "Price must be positive";
              assert quantity > 0 : "Quantity must be positive";
              assert taxRate >= 0 : "Tax rate must be non-negative";
              return price * quantity * (1 + taxRate);
          }
      }
  3. 测试:

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

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

练习

基础练习题

  1. 引入简单断言

    • 给定以下 Java 代码,processUserInput方法中有一个隐含的假设。请引入断言来明确表达这个假设。

      java 复制代码
        public class UserProcessor {
          private String birthYearInput;
      
          public UserProcessor(String birthYearInput) {
              this.birthYearInput = birthYearInput;
          }
      
          public void processUserInput() {
              assert birthYearInput != null : "Birth year input must not be null";
              int age = 2025 - Integer.parseInt(birthYearInput);
              System.out.println("User's age is: " + age);
              if (age >= 18) {
                  System.out.println("User is an adult.");
              } else {
                  System.out.println("User is a minor.");
              }
          }
      }
  2. 引入复杂条件断言

    • 下面的 Java 代码中有两个方法calculateRectangleArea和calculateSquareArea,都包含隐含的假设。请引入断言来明确表达这些假设。

      java 复制代码
        public class ShapeCalculator {
          public void calculateRectangleArea(int length, int width) {
              assert length > 0 : "Length must be positive";
              assert width > 0 : "Width must be positive";
              System.out.println("Rectangle area is: " + (length * width));
          }
      
          public void calculateSquareArea(int side) {
              assert side > 0 : "Side must be positive";
              System.out.println("Square area is: " + (side * side));
          }
      }

进阶练习题

  1. 引入复杂逻辑断言

    • 在这段 Java 代码中,calculateShippingCost方法内有一个隐含的假设。请引入断言来明确表达这个假设。

      java 复制代码
        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() {
              assert baseShippingCost > 0 : "Base shipping cost must be positive";
              assert orderQuantity > 0 : "Order quantity must be positive";
              assert discountRate >= 0 : "Discount rate must be non-negative";
              return baseShippingCost - (baseShippingCost * discountRate) - (orderQuantity > 5 ? 5 : 0);
          }
      }
  2. 引入方法与返回值断言

    • 给定以下 Java 代码,processData方法包含一个隐含的假设。请引入断言来明确表达这个假设。

      java 复制代码
        import java.util.ArrayList;
      import java.util.List;
      
      public class DataProcessor {
          public int processData(int[] dataArray) {
              assert dataArray != null : "Data array must not be null";
              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;
          }
      }

综合拓展练习题

  1. 多模块引入断言与代码审查模拟
    • 考虑一个简单的 Java 电商系统,有Product类、Cart类和Order类。Cart类中的calculateCartTotal方法和Order类中的calculateOrderTotal方法都有隐含的假设,同时Cart类中的applyCartDiscount方法也有隐含的假设。

    • 请对这些方法进行 "引入断言" 重构,将隐含的假设明确表达。

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

      java 复制代码
        class Product {
          private double price;
      
          public Product(double price) {
              assert price > 0 : "Price must be positive";
              this.price = price;
          }
      
          public double getPrice() {
              return price;
          }
      }
      
      class Cart {
          private Product[] products;
          private double discountRate;
      
          public Cart(Product[] products, double discountRate) {
              assert products != null : "Products must not be null";
              assert discountRate >= 0 : "Discount rate must be non-negative";
              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) {
              assert products != null : "Products must not be null";
              assert shippingCost >= 0 : "Shipping cost must be non-negative";
              this.products = products;
              this.shippingCost = shippingCost;
          }
      
          public double calculateOrderTotal() {
              double total = 0;
              for (Product product : products) {
                  total += product.getPrice();
              }
              return total + shippingCost;
          }
      }
相关推荐
林鹿4 分钟前
Dart: 串联多个数据流
后端·架构·dart
Java水解25 分钟前
MySQL 分页查询优化
后端·mysql
想用offer打牌41 分钟前
面试官拷打我线程池,我这样回答😗
java·后端·面试
用户6945295521701 小时前
国内开源版“Manus”——AiPy实测:让你的工作生活走上“智动”化
前端·后端
重庆小透明1 小时前
【从零学习JVM|第三篇】类的生命周期(高频面试题)
java·jvm·后端·学习
寻月隐君1 小时前
Rust + Protobuf:从零打造高效键值存储项目
后端·rust·github
radient1 小时前
Java/Go双修 - Go哈希表map原理
后端
陈随易1 小时前
Gitea v1.24.0发布,自建github神器
前端·后端·程序员
前端付豪2 小时前
汇丰银行技术架构揭秘:全球交易稳定背后的“微服务+容灾+零信任安全体系”
前端·后端·架构
于顾而言2 小时前
【Map Or Rewrite】Nginx基于会话转发的一些实践
后端