简介
"用对象替换基本类型"是提升领域建模能力的关键重构手法。通过将简单值类型封装为领域对象,可以赋予数据相关行为,增强类型安全性并减少"基本类型偏执"坏味道。
针对的症状(代码坏味道)
- 基本类型数据需要关联行为操作
- 相同含义的值在多处重复验证
- 存在表示领域概念的原始值
- 需要封装数据校验规则
用对象替换基本类型的详细步骤
- 创建值对象类
- 声明final字段存储原始值
- 添加参数校验构造方法
- 迁移相关行为
- 将处理该值的函数移至新类
- 保持原始值的不可变性
- 替换类型引用
- 用新对象替换原始类型声明
- 更新相关的getter/setter
- 保持兼容接口
- 添加toPrimitive()转换方法
- 重写toString()/equals()等方法
示例
重构前代码:
class Customer {
private String zipCode; // 格式:123-4567
boolean isValidZipCode() {
return zipCode.matches("\\d{3}-\\d{4}");
}
String getDeliveryRegion() {
return zipCode.substring(0, 3);
}
}
重构步骤:
-
创建ZipCode值对象:
class ZipCode { private final String value; public ZipCode(String value) { if (!value.matches("\\d{3}-\\d{4}")) { throw new IllegalArgumentException("Invalid zipcode format"); } this.value = value; } String getRegion() { return value.substring(0, 3); } @Override public String toString() { return value; } } -
重构Customer类:
class Customer { private ZipCode zipCode; boolean isValidZipCode() { return zipCode != null; } String getDeliveryRegion() { return zipCode.getRegion(); } }
练习
基础练习题
-
简单类型替换
// 重构前 class Product { private String sku; // 格式:CAT-001 }
进阶练习题
-
带行为的值对象
// 重构前 class Order { double calculateTax(double amount, String countryCode) { if ("US".equals(countryCode)) return amount * 0.07; if ("JP".equals(countryCode)) return amount * 0.08; return 0; } }
综合拓展练习题
-
复合值对象
// 重构前 class Person { private String idNumber; // 包含生日、性别等信息 int getBirthYear() { return Integer.parseInt(idNumber.substring(0, 4)); } }
代码审查要点
-
优点:
- 增强领域模型表现力
- 集中数据校验逻辑
- 避免基本类型滥用
-
潜在问题:
- 控制值对象膨胀
- 保持合理的构造成本
- 注意序列化兼容性