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

简介

"引入特殊情况类"(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;
         }
      }
相关推荐
brzhang18 分钟前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构
brzhang23 分钟前
一文说明白为什么现在 AI Agent 都把重点放在上下文工程(context engineering)上?
前端·后端·架构
Roye_ack1 小时前
【项目实战 Day9】springboot + vue 苍穹外卖系统(用户端订单模块 + 商家端订单管理模块 完结)
java·vue.js·spring boot·后端·mybatis
AAA修煤气灶刘哥2 小时前
面试必问的CAS和ConcurrentHashMap,你搞懂了吗?
后端·面试
SirLancelot13 小时前
MinIO-基本介绍(一)基本概念、特点、适用场景
后端·云原生·中间件·容器·aws·对象存储·minio
golang学习记3 小时前
Go 1.25 新特性:正式支持 Git 仓库子目录作为 Go 模块
后端
Penge6664 小时前
一文读懂 ucrypto.Md5
后端
Terio_my6 小时前
Spring Boot 缓存集成实践
spring boot·后端·缓存
karry_k6 小时前
JMM与Volatitle
后端
数字化顾问7 小时前
“AMQP协议深度解析:消息队列背后的通信魔法”之核心概念与SpringBoot落地实战
开发语言·后端·ruby