重构手法——函数行为重塑类 | 语意 | 用对象替换数据值

44 阅读2分钟

简介

"用对象替换数据值"是深化领域驱动设计的重要重构手法。通过将原始数据组合封装为领域对象,可以建立更精确的业务模型,并集中处理相关数据操作。

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

  • 多个相关联的原始值总是一起出现
  • 需要维护数据间的约束关系
  • 相同的数据组合在多处重复操作
  • 存在表示业务实体的数据组

用对象替换数据值的详细步骤

  1. 识别数据组合
    • 查找总是一起出现的原始值
    • 标记具有业务含义的数据组
  2. 创建值对象类
    • 声明final字段存储原始值
    • 添加数据校验逻辑
  3. 迁移关联行为
    • 将数据处理逻辑移至新类
    • 保持值对象不可变性
  4. 替换原始引用
    • 用新对象替换原始数据组合
    • 更新相关方法签名

示例

重构前代码:

class Order {
    private String customerName;
    private String customerPhone;

    String getContactInfo() {
        return customerName + " " + customerPhone;
    }

    void validate() {
        if (customerPhone.length() != 11) {
            throw new IllegalArgumentException("Invalid phone");
        }
    }
}

重构步骤:

  1. 创建Customer值对象:

    class Customer {
        private final String name;
        private final String phone;
    
        public Customer(String name, String phone) {
            if (phone.length() != 11) {
                throw new IllegalArgumentException("Invalid phone");
            }
            this.name = name;
            this.phone = phone;
        }
    
        String getContactInfo() {
            return name + " " + phone;
        }
    }
    
  2. 重构Order类:

    class Order {
        private Customer customer;
    
        void validate() {
            // 验证逻辑移至Customer类
        }
    }
    

练习

基础练习题

  1. 简单数据组合替换

    // 重构前
    class Product {
        private String category;
        private String skuCode;
    }
    

进阶练习题

  1. 带约束的数据对象

    // 重构前
    class BankAccount {
        void transfer(String targetAccount, String targetBank, double amount) {
            if (targetAccount.length() != 16) {
                throw new IllegalArgumentException("Invalid account");
            }
            // 转账逻辑
        }
    }
    

综合拓展练习题

  1. 复合业务对象

    // 重构前
    class DeliveryService {
        void schedule(String street, String city, LocalDateTime windowStart, LocalDateTime windowEnd) {
            if (windowEnd.isBefore(windowStart)) {
                throw new IllegalArgumentException("Invalid time window");
            }
            // 排期逻辑
        }
    }
    

代码审查要点

  1. 优点:

    • 明确业务概念边界
    • 集中数据约束规则
    • 减少数据传递参数
  2. 潜在问题:

    • 控制对象粒度大小
    • 保持合理的构造成本
    • 注意跨聚合的引用