重构手法——代码健壮性增强类 | 防御性编程 | 引入参数对象

68 阅读2分钟

简介

"引入参数对象"(Introduce Parameter Object)是一种通过将多个相关参数封装为独立对象来简化方法签名的重构手法。这种方法特别适用于处理包含多个关联参数的方法。

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

  • 过长参数列表(Long Parameter List)
  • 重复的参数组(Repeated Parameter Groups)
  • 数据泥团(Data Clumps)

引入参数对象的详细步骤

  1. 识别相关参数组

    • 寻找频繁同时出现的参数
    • 识别属于同一概念范畴的参数
    • 验证参数组的逻辑关联性
  2. 创建参数类

    • 定义新的值对象(Value Object)
    • 将相关参数作为类的字段
    • 添加必要的验证逻辑
    • 实现equals()和hashCode()方法
  3. 替换方法参数

    • 修改方法签名使用新参数对象
    • 更新方法内部对参数的引用方式
    • 保持旧方法的重载版本(可选)
  4. 更新调用方

    • 在所有调用处创建参数对象实例
    • 逐步替换旧参数传递方式
    • 移除过时的参数(当所有调用方迁移完成后)
  5. 后续优化

    • 将相关行为迁移到参数类
    • 添加构建方法简化对象创建
    • 考虑不可变性设计

示例

假设有个订单处理类包含多个日期参数:

public class OrderProcessor {
    public void validateOrder(Date startDate,
                              Date endDate,
                              Date paymentDate,
                              Date deliveryDate) {
        // 复杂的日期验证逻辑
    }
}

重构步骤:

  1. 创建日期范围参数对象:

    public class DateRange {
        private final Date start;
        private final Date end;
    
        public DateRange(Date start, Date end) {
            this.start = start;
            this.end = end;
        }
    
        // 添加验证逻辑
        public boolean isValid() {
            return start.before(end);
        }
    }
    
  2. 重构原始方法:

public class OrderProcessor {
    public void validateOrder(DateRange orderPeriod,
                              Date paymentDate,
                              DateRange deliveryWindow) {
        if (!orderPeriod.isValid()) {
            throw new IllegalArgumentException();
        }
        // 更新后的验证逻辑
    }
}

练习

基础练习题

  1. 用户信息参数封装
    • 重构以下方法,将用户基本信息封装为UserInfo对象:

      public class UserService {
          public void createUser(String username,
                                 String email,
                                 String phone,
                                 LocalDate birthDate) {
              // 用户创建逻辑
          }
      }
      

进阶练习题

  1. 订单参数对象设计
    • 将以下订单参数重构为复合对象:

      public class OrderSystem {
          public void processOrder(String customerId,
                                   BigDecimal totalAmount,
                                   String shippingAddress,
                                   String billingAddress,
                                   LocalDateTime orderTime) {
              // 订单处理逻辑
          }
      }
      

综合拓展练习

  1. 多模块参数重构
    • 在电商系统中,PaymentService和ShippingService都包含以下参数:

      • recipientName
      • streetAddress
      • city
      • postalCode
      • countryCode
    • 创建Address参数对象并重构相关服务类

    • 模拟代码审查报告,指出参数对象的边界验证问题

      class PaymentService {
          public void processPayment(String recipientName,
                                     String streetAddress,
                                     String city,
                                     String postalCode,
                                     String countryCode,
                                     BigDecimal amount) {
              // 支付处理逻辑
          }
      }
      
      class ShippingService {
          public void scheduleDelivery(String recipientName,
                                       String streetAddress,
                                       String city,
                                       String postalCode,
                                       String countryCode,
                                       LocalDateTime deliveryTime) {
              // 物流安排逻辑
          }
      }