Skip to content

值对象

实现值对象,就是实现自定义的数据类型,类似于很多语言中的字符串类型。字符串是一个复杂的数据类型,定义出这个类型,大大简化了编程。一样的,多定义类似的对象来表达业务中的数据概念,也一样能大大简化编程。

建模过程没有那么强调值对象,所以值对象实际不重要?

不是的。值对象对于实现领域模型非常重要。如果不定义好值对象,会导致开发领域模型成本更高,也降低了领域模型的可读性和可维护性。

即使不使用DDD,也应该大量定义值对象,就能够起到提升代码质量,降低开发成本的目的。

值对象里可以有函数吗?

可以有。和字符串对象类似,自定义的数值类型,也可以有自己的围绕数值的函数。

定义和实现值对象有什么好处?

  • 通过类型显式表达业务含义,提升代码可读性,减少错误。比如订单编号是一个字符串,如果显式的定义了一个订单编号的类,则阅读代码的人,通过类型就知道变量的值一定是一个订单编号。避免错误的把一个合同编号复制给订单编号,因为类型不一致,赋值会出错。
  • 确保格式正确,避免到处校验。订单编号是有固定格式的,当我们创建这个订单编号实例的时候,就会对它的格式做校验,这样确保所有其它地方代码拿到订单编号实例时,拿到的一定是符合格式的订单编号。如果不定义类型,而是直接用字符串,那就没有办法保证拿到的字符串是符合订单编号的格式的,经常为了避免出错反复的去校验格式。
  • 减少数据复制。外部系统的数据进入系统后,就立刻转成值对象,之后代码都引用值对象对数据进行操作,减少了一个字段一个字段的反复复制。
  • 避免隐式修改引发的bug。 常见的一个bug是,把一个计算好的对象传递给另外一个函数,这个函数修改了这个对象,后面写代码的人不知道前面的函数修改了对象,因为从函数定义中看不出来会修改这个对象,导致bug出现。值对象的不可变,确保此类情况不会发生,因为函数不能修改这个对象,如果它要修改,只能复制一份修改了用。
  • 安全并发。因为不会被修改,可以放心的无锁的在多线程中并发访问。

如何实现不可变值对象?

只能在构造的时候设定值,不提供修改内部属性的函数,就成了不可变对象。

领域模块外部可以使用值对象吗?

由于值对象不可变的特性,是可以安全的在领域模块之外使用的。但是和其它领域模型的代码一样,一般不推荐被别的服务的代码直接引用,因为如果这样的话,修改一个服务的值对象的代码,可能会影响到别的服务,容易引发bug。如果多个服务要共享的话,需要加倍小心。

值对象是不可变的,但是业务上确实要改变某个字段值啊?

假如某个业务对象的某个字段是一个值对象,我们期望修改这个值对象内部的属性,但是不能修改,那么就改成把这个业务对象的这个属性的值整体修改成另外一个值对象。

值对象是不是就是DTO?

DTO是对象的一种角色,用来做数据传输,定义数据传输格式的对象,都可以被称之为DTO。如果值对象的结构是符合数据传输要求的,那么让值对象承担起DTO的角色,也是可以的。但是,当数据传输格式变化,值对象不能承担这个责任的时候,应该定义一个专门的DTO对象,而不是为了数据传输去污染值对象。

使用值对象,导致对象嵌套了,ORM JSON序列化等都不好用了,怎么办?

换用功能更强大的ORM JSON等框架,如果仍旧不能满足,那还是定义专门的对象来和ORM映射吧。