值对象
付出的是价格,获得的是价值。 -—Warren Buffett
值对象虽然经常被掩盖在实体的阴影之下,但它却是非常重要的DDD 部件。值对象的常见例子包括数字,比如3、10和293.51;或者文本字符串,比如“hello, world”和“Domain-Driven Design” ;或者日期、时间,还有更加详细的对象,比如某人的全名,其中包含姓氏、名字和头衔,再比如货币、颜色、电话号码和邮寄地址等。当然还有更加复杂的值对象。在本章中,我们将讨论那些能够反映通用语言概念的值对象。
认识值类型的优点 值类型用于度量和描述事物,我们可以非常容易地对值对象进行创建、测试、使用、优 化和维护。
我们应该尽量使用值对象来建模而不是实体对象,你可能对此非常惊讶。即 便一个领域概念必须建模成实体,在设计时也应该更偏向于将其作为值对象容器,而不是子实体容器。这并不是源自于无端的偏好,而是因为我们可以非常容易地对值对象进行创建、测试、使用、优化和维护。
本章学习路线图
- 学习如何将一个领域概念建模成值对象。
- 学习如何通过值对象来简化集成的复杂性。
- 学习以值对象来创建领域标准类型。
- 学习SaaSOvation是如何学到值对象的重要性的。
- 学习SaaSOvation是如何测试、实现和持久化值对象的。
开始,SaaSOvation公司的团队滥用了头 体建模。事实上.在用户和权限等概念进入协作领域之前,实体建模并没有给他们带来什么坏处。在项目启动时.他们采用了常用的建模方式.即将领域模型中所有的属性都映射到对应的数据库表中.并且为所有的属性创建setter和getter* 方法。由于每个对象都有一个数据库主键.各个实体被组织在了一个庞大且复杂的对象网中。这种建模方式是一种数据建模方式.它在很大程度上受到了关系型数据库的影响.认为所有的东西都需要范式化.并且通过外键进行关联引用。后来,SaaSOvation团队成员们才知道.全然面向实体的思维方法不仅没有必要,而且还浪费开发时间。
在设计得当的情况下,我们可以对值对象实例进行创建和传递,甚至在使用 完之后将其直接扔掉。我们不用担心客户端对值对象的修改。一个值对象的生命周期可长可短,它就像一个无害的过客在系统中来来往往。
从这个角度来看待值对象是一个很大的转变,就像从没有垃圾回收机制的语 言转变到有垃圾回收机制的语言一样。
那么,我们如何确定一个领域概念应该建模成一个值对象呢?此时我们需要 密切关注值对象的特征。
当你只关心某个对象的属性时,该对象便可作为一个值对象。为其添加有意义的属 性,并赋予它相应的行为。我们需要将值对象看成不变对象,不要给它任何身份标识,
还应该尽量避免像实体对象一样的复杂性。[Evans,p.99]
虽然创建一个值对象类型非常简单,但是有时甚至连有经验的DDD开发者都 面临这样一个难题:是应该建模成实体昵还是值对象?和如何实现值对象一道,我希望在本章中教你理清这些含糊的概念。