值对象(value objectobject)是快照,固定不变,对象创建完成之后,就不能修改里面的所有属性。
比如,今天吃饭花了 20 块钱,就可以定义成值对象,因为吃饭花了 20 块钱已经是事实,不会再有变化。
值对象的常见例子包括数字,比如3、10和293.51;或者文本字符串,比如“hello,world”和“Domain-Driven Design”;或者日期、时间;还有更加详细的对象,比如某人的全名,其中包含姓氏、名字和头衔;再比如货币、颜色、电话号码和邮寄地址等。
值对象,也是用来模拟现实世界使用。
现实世界中的物事,在生命周期内有变化的事物用实体来建模,在生命周期内无变化的事物用值对象来建模。
比如,常用的审计日志、用户操作日志,这些就很适合使用值对象来建模。
比如,用户每一笔消费记录,也很适合用值对象来建模。
比如,保证书、保密协议、签名,也很适合用值对象来建模。

值对象与实体的区别
值对象与实体的区别是,值对象在生命周期内属性不能变,实体在生命周期内属性可以变。
值对象能有唯一标识吗?
值对象可以有唯一标识,但是唯一标识不是值对象必须拥有的。
为什么说值对象可以有唯一标识呢?既然值对象也是真实物理世界的映射,那么,每一笔消费记录在真实世界中都有一个唯一的流水号,难道这个流水号,不是唯一标识吗?
值对象,能存储到数据库中吗?
值对象能存储到数据库中,存储到数据库中的数据并不只有实体,领域事件也能存储到数据库中。
值对象,能有行为方法吗?
值对象中能有行为方法,但行为方法不能改变值对象内容的属性值。举个例子,身份证,可以建模成值对象,身份证值对象有个行为方法获取身份证号码,但是因为信息敏感的原因,获取身份证号码这个行为方法,返回的是掩码后的身份证号。
值对象,能被修改吗?
因为值对象是真实世界的快照,因此值对象是固定的,不能被修改的,值对象内部的所有属性,也都不能被修改。 在Java中,值对象的Java类中,属性不能有public、protected的set方法,同时最好被final修饰。
import lombok.Getter;
@Getter
public class IdCardVO
{
// 身份证号码
private final String cardNo;
// 名字
private final String personName;
// 民族
private final String nation;
// 工址
private final String address;
public IdCardVO(String cardNo, String personName, String nation, String address) {
// 检查数据是否正确
if (null == cardNo || personName == null || nation == null || address == null || cardNo.length() != 18){
throw new IllegalArgumentException("参数不正确");
}
this.cardNo = cardNo;
this.personName = personName;
this.nation = nation; this.address = address;
}
/**
* 返回身份证号码(打上掩码)
*
* @return 身份证号码
*/
public String cardNo() { return cardNo.substring(0, 13) + "*****"; }
}
什么时候该用值对象?
1、用于方法传参
相信很多写过代码的同学,都经常遇到某个方法的名字叫做getXxx(params),但却是在getXxxx方法里面修改了params的属性值……。因此,当需要排查问题的时候,需要每个方法都点击进去一行行看代码,因为getXxxx方法修改params的属性值这种场景很常见。
理想的状态下,我们是不希望传入的参数被方法内部修改的。这时候,我们就可以使用值对象作为传参,因为值对象具有不可被修改的性质,因此很适合用于方法传参。
2、用于对真实世界中属性不可被改变的对象进行建模
因为值对象的本质是映射真实世界中属性不可被改变的对象,所以很适合用来对这类对象进行建模
3、用于系统集成的传参与返回值
无论系统集成之间是通过消息队列这种方式,还是通过API这种方式,还是其他的方式,都要求参数(请求参数和返回参数)具有不可被修改的性质,因此很适合使用值对象。
值对象中真的不能有set方法吗?
很多json之类框架,都要求Java类中提供get、set方法。很多orm之类的框架,也要求Java类中提供get、set方法。很多人因为这些框架的限制set方法,而不敢使用值对象。
如果你的思想被教条主义所局限了,那么,值对象中不能有任何set方法。
DDD是一个方法论,值对象是其中一个概念。
难道Java实体中有set方法就不能是值对象了吗?我们在编写的时候严格执行,只要是被定义成值对象的Java类,都不能去调用里面的set方法,这难道不是在践行DDD值对象的本质吗?
如果,所用开发语言是Javascript呢?Javascript可没有提供像Java一样的private语法对属性进行封装,难道Javascript就不能定义值对象了吗?我们在编写的时候严格执行,只要是被定义成值对象的Javascript对象,都不能去更改里面的属性值,这难道不是在践行DDD值对象的本质吗?
因此,值对象中能有set方法,但是,在编码的时候,必须要人为限制,不能写代码去调用set方法,set方法只能由orm、gson之类的框架调用!