再谈值对象
值对象是领域模型中比较难以理解的部分,这也不难怪,因为我们已经习惯与数据库打交道了,习惯了 类-表 对应的关系。既然一个对象要保存到数据库里,那它不就是实体吗??
好的,首先,忘记数据库,忘记!
我们使用jdon上一个例子来说明。有个帖子说了,目前系统中有相册这么一个功能,这个相册会被许多用户对象所引用,那么这个“相册”应该怎么设计呢?banq的答案是:把相册设计成值对象。
Ok,很多人看了以后,觉得这个答案很好,可以未必真的完全理解里面的意义。换个场景,就未必能想出答案了。
所以,首先,忘记数据库。
我们来设计一个系统,没有数据库的系统。所有的数据都存在于内存中。你不用管持久化的问题。
这个系统的功能是用户可以拥有自己的相册,可以收藏别人的相册。所以,很显然,我们有两个实体对象:用户和相册。
Public class Users {}
Public class Photo{}
没错,相册是实体,因为它有唯一标示符。
现在我们继续设计,用户会拥有自己的相册:
Public class Users {
Private List<Photo> myPhotoList;
}
嗯,用户还拥有对别人相册的引用:
Public class Users {
Private List<Photo> myPhotoList;
Private List<Photo> otherPhotoList;
}
其中 myPhotoList 是对好多个 Photo对象的应用。你问我“好多个 Photo对象”在哪里?ok,“好多个 Photo对象”在这个例子里,不是在数据库里,是在内存里。
例如我们创建了一个 MemDB 类:
Public class MemDB {
List<Photo> photoList;
}
这个 photoList 就是“好多个 Photo对象”,而这个 MemDB 就是我们持久化实体的地方。
用户对于自己的相册,也就是 myPhotoList,应该可以修改,删除,甚至增加等等。而对myPhotoList里的对象的修改,应该如实地反映到 MemDB里的photoList里。所以,myPhotoList里的对象就是实体。
用于对于收藏他人的相册,也就是otherPhotoList,只能看,或许可以修改,但也是修改给自己看,这些修改不应该反映到MemDB里的photoList里。所以,otherPhotoList里的对象就是值对象!从实现方式来说,应该是Photo对象的拷贝,而不是引用。当然,如果系统有这么一个特殊要求,收藏别人的相册,也可以进行部分的修改,那么otherPhotoList就是实体!
我想大家一定明白了。
不过,回归到现实,我们必须使用数据库,对不?
所以,otherPhotoList也好,myPhotoList也好,在代码层面上来说,都是值对象。为什么?因为当你修改了myPhotoList里的对象,并不会立刻反映到持久层,关键是你是否调用了存储他们的方法。这也是 DDD 与目前的技术无法一致的一个方面。
虽然如此,在分析阶段我们也有必要分清楚实体和值对象,毕竟我们可以遵循一些原则,如值对象在大部分情况下是无需保存的等等。这对我们能够清晰的了解系统有很大的好处。
注:附件中的pdf带有颜色等格式,方便阅读。
attachment:
think_of_value_object.zip