在测试驱动开发的环境下,当设计一个复杂业务中的值对象时,应当如何确保其不可变性以及等同性比较的方式正确无误?给出一个具体的实现例子。
在测试驱动开发(TDD)的环境下,确保值对象的不可变性和等同性比较的正确无误是至关重要的。值对象的不可变性意味着一旦对象创建后其状态就不能被改变,等同性则表示值对象的内容相同即视为等价,而不考虑对象的身份标识。下面通过一个具体的Java实现例子来说明如何实现这些特性。
定义值对象
首先,我们定义一个表示货币金额的值对象Money,它包含金额数量amount和货币类型currency两个属性。
public final class Money {
private final BigDecimal amount;
private final String currency;
public Money(BigDecimal amount, String currency) {
if (amount == null || currency == null) {
throw new IllegalArgumentException("Amount and currency cannot be null");
}
this.amount = amount;
this.currency = currency;
}
public BigDecimal getAmount() {
return amount;
}
public String getCurrency() {
return currency;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Money money = (Money) o;
return amount.equals(money.amount) &&
currency.equals(money.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
@Override
public String toString() {
return "Money{" +
"amount=" + amount +
", currency='" + currency + '\'' +
'}';
}
}
分析
- 构造函数:
Money类的构造函数是私有的,只在创建对象时接受参数,且对空值进行了保护,保证了对象创建后的不可变性。 - 访问器方法:提供只读的访问方法,无任何setter或修改状态的方法,保持了不可变性。
- equals和hashCode:重写了
equals和hashCode方法,确保当两个Money对象的amount和currency属性完全相同时,它们被认为是等同的。equals方法首先检查是否为同一个对象引用,然后检查对象类型,最后比较属性值。hashCode方法根据属性值计算哈希值,保证了在哈希集合中能够正确地分辨不同的值对象。 - toString:重写
toString方法便于调试和日志记录。
通过这种方式,Money值对象既保持了不可变性,又正确实现了等同性比较。在TDD的过程中,应当为这些方法编写相应的单元测试,确保它们的行为符合预期。例如,可以编写测试用例验证两个相同属性的Money对象确实在equals方法中返回true,以及它们的hashCode值是否相同。