重构手法——代码健壮性增强类 | 防御性编程 | 引入特殊情况类

简介

"引入特殊情况类"(Introduce Special Case)是一种重构手法,通过创建一个特殊的类来处理某些特殊情况,从而减少代码中的重复条件检查。这种方法特别适用于那些需要频繁检查某个对象是否为 null 或其他特殊值的情况。以下是进行"引入特殊情况类"重构的详细步骤:

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

  • 重复的条件检查(Repeated Conditional Checks)
  • 过多的 null 检查(Excessive Null Checks)

引入特殊情况类(Introduce Special Case)的详细步骤

  1. 识别需要处理的特殊情况
    • 寻找代码中频繁出现的条件检查,特别是 null 检查。
    • 确定这些条件检查是否可以被封装到一个特殊的类中。
  2. 创建特殊情况类
    • 创建一个新的类来表示特殊情况。
    • 为这个类实现与正常情况相同的接口或方法,但返回特殊值或执行特殊操作。
  3. 替换条件检查
    • 将代码中的条件检查替换为对特殊情况类的使用。
    • 确保所有相关的地方都使用新的特殊情况类。
  4. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
    • 手动测试:如果有必要,进行手动测试以验证功能的正确性。
  5. 代码审查
    • 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
    • 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明引入特殊情况类的影响。

示例

假设有一个方法 getCustomerName,其中包含对 null 的频繁检查,我们希望对其进行"引入特殊情况类"的重构:

java 复制代码
public class Customer {
   private String name;

   public Customer(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}

public class Order {
   private Customer customer;

   public Order(Customer customer) {
      this.customer = customer;
   }

   public String getCustomerName() {
      return (customer == null) ? "Unknown Customer" : customer.getName();
   }
}

步骤如下:

  1. 识别需要处理的特殊情况:

    • 频繁的 null 检查:customer == null
  2. 创建特殊情况类:

    • 创建一个新的类 UnknownCustomer 来表示特殊情况:

      java 复制代码
        public class UnknownCustomer extends Customer {
         public UnknownCustomer() {
            super("Unknown Customer");
         }
      }
  3. 替换条件检查:

    • null 检查替换为对 UnknownCustomer 的使用:

      java 复制代码
        public class Order {
         private Customer customer;
      
         public Order(Customer customer) {
            this.customer = (customer == null) ? new UnknownCustomer() : customer;
         }
      
         public String getCustomerName() {
            return customer.getName();
         }
      }
  4. 测试:

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

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

练习

基础练习题

  1. 引入简单特殊情况类

    • 给定以下 Java 代码,getProductPrice方法中有频繁的 null 检查。请引入特殊情况类来封装这些检查。

      java 复制代码
        public class Product {
         private double price;
      
         public Product(double price) {
            this.price = price;
         }
      
         public double getPrice() {
            return price;
         }
      }
      
      public class Order {
         private Product product;
      
         public Order(Product product) {
            this.product = product;
         }
      
         public double getProductPrice() {
            return (product == null) ? 0.0 : product.getPrice();
         }
      }
  2. 引入复杂特殊情况类

    • 下面的 Java 代码中有两个方法getCustomerName和getCustomerAddress,都包含频繁的 null 检查。请引入特殊情况类来封装这些检查。

      java 复制代码
        public class Customer {
         private String name;
         private String address;
      
         public Customer(String name, String address) {
            this.name = name;
            this.address = address;
         }
      
         public String getName() {
            return name;
         }
      
         public String getAddress() {
            return address;
         }
      }
      
      public class Order {
         private Customer customer;
      
         public Order(Customer customer) {
            this.customer = customer;
         }
      
         public String getCustomerName() {
            return (customer == null) ? "Unknown Customer" : customer.getName();
         }
      
         public String getCustomerAddress() {
            return (customer == null) ? "Unknown Address" : customer.getAddress();
         }
      }

进阶练习题

  1. 引入复杂逻辑特殊情况类

    • 在这段 Java 代码中,calculateShippingCost方法内有频繁的 null 检查。请引入特殊情况类来封装这些检查。

      java 复制代码
        public class ShippingInfo {
         private double baseShippingCost;
         private double discountRate;
      
         public ShippingInfo(double baseShippingCost, double discountRate) {
            this.baseShippingCost = baseShippingCost;
            this.discountRate = discountRate;
         }
      
         public double getBaseShippingCost() {
            return baseShippingCost;
         }
      
         public double getDiscountRate() {
            return discountRate;
         }
      }
      
      public class Order {
         private ShippingInfo shippingInfo;
      
         public Order(ShippingInfo shippingInfo) {
            this.shippingInfo = shippingInfo;
         }
      
         public double calculateShippingCost() {
            return (shippingInfo == null) ? 0.0 : shippingInfo.getBaseShippingCost() - (shippingInfo.getBaseShippingCost() * shippingInfo.getDiscountRate());
         }
      }
  2. 引入方法与返回值特殊情况类

    • 给定以下 Java 代码,processData方法包含频繁的 null 检查。请引入特殊情况类来封装这些检查。

      java 复制代码
        import java.util.ArrayList;
      import java.util.List;
      
      public class DataProcessor {
         public int processData(int[] dataArray) {
            if (dataArray == null) {
               return 0;
            }
            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方法都有频繁的 null 检查,同时Cart类中的applyCartDiscount方法也有频繁的 null 检查。

    • 请对这些方法进行 "引入特殊情况类" 重构,将频繁的 null 检查封装到特殊情况类中。

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

      java 复制代码
        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() {
            if (products == null) {
               return 0.0;
            }
            double total = 0;
            for (Product product : products) {
               if (product == null) {
                  continue;
               }
               total += product.getPrice();
            }
            return total - (total * discountRate);
         }
      
         public void applyCartDiscount() {
            if (products == null) {
               return;
            }
            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) {
            this.products = products;
            this.shippingCost = shippingCost;
         }
      
         public double calculateOrderTotal() {
            if (products == null) {
               return 0.0;
            }
            double total = 0;
            for (Product product : products) {
               if (product == null) {
                  continue;
               }
               total += product.getPrice();
            }
            return total + shippingCost;
         }
      }
相关推荐
你的人类朋友4 分钟前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴5 分钟前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___22 分钟前
日期的数据格式转换
前端·后端·学习·node.js·node
保持学习ing2 小时前
SpringBoot前后台交互 -- 登录功能实现(拦截器+异常捕获器)
java·spring boot·后端·ssm·交互·拦截器·异常捕获器
十年老菜鸟3 小时前
spring boot源码和lib分开打包
spring boot·后端·maven
白宇横流学长4 小时前
基于SpringBoot实现的课程答疑系统设计与实现【源码+文档】
java·spring boot·后端
加瓦点灯4 小时前
什么?工作五年还不了解SafePoint?
后端
他日若遂凌云志5 小时前
Lua 模块系统的前世今生:从 module () 到 local _M 的迭代
后端
David爱编程5 小时前
Docker 安全全揭秘:防逃逸、防漏洞、防越权,一篇学会容器防御!
后端·docker·容器
小码编匠5 小时前
WinForm 工业自动化上位机通用框架:注册登录及主界面切换实现
后端·c#·.net