请阐述值对象 (Value Object) 的特点,并与实体 (Entity) 进行对比,指出两者主要的区别在哪里?

值对象 (Value Object) 的特点

  • 不变性:值对象一旦创建就不能修改,这意味着它们是不可变的。值对象的任何变更都会导致创建新的对象,这种特性保证了在多线程环境中使用的安全性及数据的一致性。
  • 标识与值等价性:值对象主要通过其包含的属性值来区分不同的值对象,而不是通过某个唯一的标识符。例如,两个日期时间对象如果时间相同,那么它们在逻辑上等价。
  • 简洁性:值对象通常相对较小且简单,主要用于封装那些不需要复杂行为的数据,如货币金额、电话号码等。
  • 可嵌入性:值对象通常作为实体或其他值对象的一部分存在,可以很容易地嵌入到领域模型的其他部分中。
  • 可比较性:值对象通常实现了值相等性的比较方法,例如 Java 中的 equals() 方法,以便于进行值对象之间的比较操作。

值对象与实体 (Entity) 的区别

  • 身份性:最核心的区别在于身份性。实体拥有自己唯一的标识(ID),其身份在整个生命周期中保持不变,即使实体的内部状态发生了变化,只要 ID 不变,它就还是同一个实体。而值对象没有单独的身份,它的“身份”完全由其包含的值来决定。
  • 可变性 vs 不变性:实体通常是可变的,可以修改其状态;而值对象是不可变的,一旦创建就不允许修改。如果需要“修改”值对象,实际上是创建一个新的值对象来替换旧的对象。
  • 作用范围:值对象通常具有较小的作用域,主要用于封装简单的、不变的数据。实体则拥有更广泛的作用域,通常代表系统中的主要对象或业务概念。
  • 引用 vs 传递:实体在应用中一般以引用的形式存在,通过引用可以追踪到其实例。值对象则多以值的方式传递,每次传递实际上是复制了一份新的对象,不关心是否是同一个内存地址。
  • 存储方式:在一个关系型数据库中,实体通常会有自己的表,包含自增主键等字段来标识其唯一性。值对象往往作为实体的一部分被存储,通常没有自己的表。

示例

假设我们正在开发一个在线书店应用,其中涉及到了客户 (Customer) 实体和地址 (Address) 值对象。

  • 客户 (Customer) 实体:每个 Customer 对象都有一个唯一标识符(如 customerId),即使两个客户的姓名、邮箱相同,只要 customerId 不同,那么就认为是不同的客户。这个标识符在整个客户生命周期中保持不变,即使客户的信息(如地址、电话号码)发生了变化。
  • 地址 (Address) 值对象Address 由街道、城市、省/州、邮政编码等信息组成。如果两个 Address 对象的所有属性相同,那么这两个 Address 在逻辑上是等价的,不需要关心它们是否是同一个实例。当客户更改了地址信息时,实际上是在创建一个新的 Address 对象来替换旧的地址。

通过这个例子,可以看出值对象和实体在设计和使用上的不同之处。