领域驱动设计(DDD):如何更新值对象(ValueObject)?
值对象的不可变性与更新场景处理
值对象不是完全不能"修改",而是不能原地修改——它的核心是用属性值来定义自身的身份,不像实体那样靠唯一ID区分。你没办法改动一个已经创建好的值对象,但可以基于旧对象的内容,生成一个包含更新后数据的新值对象。
比如你说的带List<String> tags的Product值对象,正确的更新方式不是往原对象的标签列表里加新标签,而是创建一个新的Product实例,把旧标签加上新标签组成的新列表传进去。
至于为什么要把字段设为final、集合设为不可修改:这是从代码层面强制保障不可变性的手段。如果不这么做,外部代码可能会不小心修改值对象的内部状态(比如直接往tags列表里加元素),破坏值对象的语义一致性——毕竟值对象的相等性是基于所有属性值的,一旦内部状态被偷偷修改,会导致各种难以追踪的bug。
给你举个实际的代码例子(Java):
public class Product { // 用final保证字段不能被重新赋值 private final List<String> tags; public Product(List<String> tags) { // 把传入的列表转成不可修改的,彻底杜绝外部修改 this.tags = Collections.unmodifiableList(new ArrayList<>(tags)); } // 提供生成新实例的方法,来实现"更新"语义 public Product addTag(String newTag) { List<String> newTagsList = new ArrayList<>(this.tags); newTagsList.add(newTag); return new Product(newTagsList); } // 只暴露只读的获取方法,不提供修改入口 public List<String> getTags() { return tags; } }
这种方式既遵守了值对象不可变的原则,又满足了更新标签的业务需求。因为新生成的Product和原来的是两个不同的值对象,对应不同的业务状态,完全符合值对象的设计理念。
总结一下:值对象的本质是值语义,不是不能更新状态,而是更新必须通过创建新实例来完成,绝对不能修改原有实例的任何属性。把字段设为final、集合设为不可修改,就是为了从根源上防止违反这个原则的操作。
内容的提问来源于stack exchange,提问作者Levi Tang
相关产品推荐
相关产品推荐

