代码改变世界

领域模型-谈实体对象和值对象

2013-05-25 18:54  youxin  阅读(7406)  评论(0编辑  收藏  举报

对于实体Entity和值对象Value Object是领域驱动设计里面两个重要的模型对象。所以有必要对两者的关系和区别进行理解。以下部分内容直接引用自《领域驱动设计》一书相关内容。

首先对于实体Entity,实体核心是用唯一的标识符来定义,而不是通过属性来定义。即即使属性完全相同也可能是两个不同的对象。同时实体本身有状态的,实体又演进的生命周期,实体本身会体现出相关的业务行为,业务行为会实体属性或状态造成影响和改变

真正的现实世界,每个事物都一定会有唯一的标识,关键点是我们实际的业务场景和需求是否需要管理到唯一标识。书里面举了一个例子,当我们发放的门票上有座位号的时候,座位需要作为独立的实体,座位号是唯一的标识。而当先到先座模式下,我们只关心剩余座位数,那么座位号并不是唯一标识。这跟我们的业务需求有关。

一个对象不由属性来定义,那么看人这个对象,身份证号是属性,其实也是对于人的唯一标识。不考虑本身身份证号的位数升级,一个身份证号会跟随你一辈子。但是对于人我们一般仍然会作为实体Entity来看待,因为人有状态,有对象演进的生命周期,会主动产生各种行为。

对于企业内信息系统,很多时候我们把员工工卡号作为唯一标识来使用,但是要意识到工卡号只是人员的一个属性。虽然工卡号本身不会出现两个重复的,但是该属性仍然可能演变,如果将工卡号作为唯一标识和ID,那么在该属性变化时候所有其余关联对象都将受到影响。从这个层面来看,一个唯一的内码ID才是可信的唯一标识。

而对于值对象Value Object,它用于描述领域的某个方面本身没有概念标识的对象,值对象被实例化后只是提供值或叫设计元素,我们只关心这些设计元素是什么?而不关心这些设计元素是谁。书里面谈到颜色,数字是常见的值对象。这种对象无状态,本身不产生行为,不存在生命周期演进。

是否为值对象跟实际的业务场景仍然关系密切。书里面又举了地址的例子,当地址是值对象的时候,地址本身无状态,可以被多个实际有状态的实体使用,地址不存在太多的生命周期演进场景下地址为值对象。而对于本身行政区域管理软件中,地址本身存在状态,存在根据行政区域规划变化而演进的过程,因此地址为实体。

如果从值对象本身无状态,不可变,并且不分配具体的标识层面来看。那么值对象可以仅仅理解为实际的Entity对象的一个属性结合而已。该值对象附属在一个实际的实体对象上面。值对象本身不存在一个独立的生命周期,也一般不会产生独立的行为。

值对象往往可能是多个属性的聚合,本身无唯一标识,多个属性最终形成的一个结果值,而这个结果值往往又依附在一个实际的实体Entity上面那么如果从这个概念来说,值对象往往不会单独进行持久化,或形成数据库设计的一张数据表。另外一种情况,对于简单的数据字典类对象,是否考虑作为值对象,这种对象需要持久化,如纳税属性,物料类型,它们设计到数据字典中取值,这个数据字典无状态,无自己的生命周期,是可以作为值对象来处理的。转自:http://blog.sina.com.cn/s/blog_493a84550101534t.html

实体:在时间上有连续性,并且有唯一标识可以来区分的对象。
值对象:用来描述事物的,不区分谁是谁的,不可变的对象。

判断一个对象是实体还是值对象,还要根据它在具体的业务领域中的实际意义来决定,比如:
体育馆里的座位,当业务领域这样规定,一张门票对应一个特定的座位,即每个座位都应该严格区分谁是谁,观众在选择座位时根据门票对应的座位号来选择这个唯一的座位,此时座位对象应该为实体。
但当业务领域改变规则,决定只要有门票,就可以进去随便坐,此时不需要明确哪个座位是哪个座位,只要有座位就可以坐下,每个座位都是同一个座位对象的副本(在某些场合可以通过共享一个对象来提高性能),无须区分谁是谁,此时座位对象应该为值对象

java开发中的值对象:

在开发java的过程中经常要用到所谓的贫血模型,即只有访问器和修改器的类(set和get方法)。今天简单的做一下思考和分析。

在面向对象开发的初期,每个对象都是由属性和动作两个部分组成,后来随着业务逻辑复杂度的升级,逐渐出现了分层,以便降低程序的逻辑和耦合性。耦合度降低的同时,原有的对象模型也被拆分,一部分是现在的值对象,也就是没有动作的贫血模型,一部分是只有动作的驱动器,于是可以将程序简单的划分为值对象和逻辑驱动两个部分,然后在逻辑驱动的基础上面再产生相应的分层。于是就有了目前所见的两层和三层的划分模式。

今天不多说层次的划分,主要将精力放在贫血模型也就是值对象上面:

值对象,简单的来说按照业务相关性可以划分为三类:

bo(bussiness object) -- 业务对象,业务相关性最强,每个对象之间的逻辑关系体现为业务的复杂性和逻辑性

vo (value object)-- 数据对象,主要用于服务层,对bo进行拆分,使其适用代码逻辑的东西

po(persistent object)-- 持久化对象,和数据库表对应的对象,用于ORM。

到此为止我想可以简单的解释为什么会在一个程序里面不停的出现各种不同的贫血模型了,因为意义不一样,当然如果一个业务逻辑是简单的,那么bo到po的重用是完全可以接受的。

通过上面的描述,我想三类定义的重点应该已经很明确了。

在设计po的时候,主要应该侧重于数据库字段和java类中的对应关系,使二者越好理解越好,比如java类中的属性可以用userName, 对应的数据库字段可以叫做user_name或者直接叫做username.

在设计bo的时候主要应该侧重于业务逻辑的关系。就是说在设计的时候直接放弃掉数据库的考量。一切以业务逻辑为主,比如某个表单,可能需要采集某个人的很多信息,然而在数据库中这些信息是需要分为常用信息和非常用信息来进行存储以便增加数据库的performance,所以可以在设计bo的时候将相关信息设计在一个类里面,但是在传递给vo的时候进行分离。

vo的设计相对要比较灵活一点,具体是侧重于po还是侧重于bo进行设计需要自己进行考虑,我的观点是在设计vo的时候考虑数据库多一点,因为这一层毕竟是解耦的