无规则简单java对象
视图对象,用于表现层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
VO通常是 Web 向模板渲染引擎层传输的对象。
对应页面显示(web页面、swt、swing界面)的数据对象。
可以和表对应,也可以不,这根据业务的需要。
数据传输对象:用于表现层与服务层之间的数据传输对象,它不应该包含业务逻辑。
DTO可以是Service 和 Manager 向外传输的对象。
大多数情况下,DTO内的数据来自多个表。
比如一张表有100个字段,那么对应的PO就有100个属性。但view 层只需显示10个字段,没有必要把整个PO对象传递到客户端,这时我们就可以用只有这10个属性的DTO来传输数据到客户端。到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO。
领域对象:从现实世界中抽象出来的有形或无形的业务实体。
在领域驱动设计中,DO不是简单的POJO,它具有领域业务逻辑。
业务对象,封装业务逻辑后的对象。
处理业务逻辑时,我们可以针对BO去处理。
BO 可以是由 Service 层输出的封装业务逻辑的对象。
业务对象主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其他的对象。
比如一个简历,有教育经历、工作经历、培训经历等等。我们可以把教育经历对应一个PO,工作经历对应一个PO,培训经历对应一个PO。建立一个对应建立的BO对象处理简历,每个BO包含这些PO。
BO可以包含多个PO,通常需要将BO转化成PO,才能进行数据的持久化,反之,从DB中得到的PO,需要转化成BO才能在业务层使用。
持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系。
PO是只含有get/set方法的POJO
如果持久层是关系型数据库,那么,对应数据库表中的实体,可以简单认为一个PO对应数据库中的一条记录,数据表中的每个字段(或若干个)对应PO的一个(或若干个)属性。
PO有时也被称为Data对象,PO中不应包含任何对数据库的操作。
数据查询对象,各层接收上层的查询请求。
注:超过2个参数的查询封装,禁止使用Map类来传输。
以一个时序图建立简单模型来描述上述对象在三层架构应用中的位置:
既然DTO是表现层与服务层之间传递数据的对象,为什么还需要一个VO呢?
对于绝大部分的应用场景来说,DTO和VO的属性值基本是一致的,而且他们通常都是POJO,因此没必要多此一举。但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在VO和DTO,因为两者有着本质的区别,DTO代表服务层需要接受的数据和返回的数据,而VO代表表现层需要显示的数据。
eg:服务层有一个getUser()方法用来返回一个系统用户,其中有一个属性是gender(性别),对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于表现层来说,它可能需要用”帅哥“代表男性,用”美女“代表女性,用”秘密“代表未指定。
说到这里,可能你还会反驳,在服务层直接就返回”帅哥 美女“不就行了吗?对于大部分应用来说,这不是问题,但设想一下,如果需求允许客户可以定制风格,而不同风格对于”性别“的表现方式不一样,又或者这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从职责单一原则来看,服务层只负责业务,与具体的表现形式无关,因此,它返回的DTO,不应该出现与表现形式的耦合。
在以下场景中,我们可以考虑把VO与DTO合二为一(注意:是实现层面):
当需求非常清晰稳定,而且客户端很明确只有一个的时候,没有必要把VO和DTO区分开来,这时候VO可以退隐,用一个DTO即可。
为什么是VO退隐而不是DTO?回到设计层面,服务层的职责依然不应该与表现层耦合,所以,DTO对于”性别“来说,依然不能用”帅哥美女“,这个转换应该依赖于页面的脚本(如JavaScript)或其他机制(JSTL、EL、CSS)。
即时客户端可以进行定制,或者存在多个不同的客户端,如果客户端能够用某种技术(脚本或者其他机制)实现转换,同样可以让VO退隐。
以下场景需要优先考虑VO、DTO并存:
因为某种技术原因,比如某个框架(如Flex)提供自动把POJO转换为UI中某些Field时,可以考虑在实现层面定义出VO。
这个 权衡完全取决于,使用框架的自动转换能力带来的开发和维护效率提升与设计多一个VO所多做的事情带来的开发和维护效率的下降之间的比对。
如果页面出现一个”大视图“,而组成这个大视图的所有数据需要调用多个服务,返回多个DTO来组装(当然,这同样可以通过服务层提供一次性返回一个大视图的DTO来取代,但在服务层提供一个这样的方法是否合适,需要在设计层面进行权衡)。
概念上的区别:DTO是表现层和服务层之间的数据传输对象(可以认为是两者之间的协议),而DO是对现实时间各种业务角色的抽象。
比如:getUser()方法返回的UserInfo不应该包含password,那么久不应该存在passwprd这个属性定义,但如果同时又一个createUser()方法,传入的userInfo需要包含用户的password。
在设计层面,表现层向服务层传递的DTO与服务层返回给表现层的DTO在概念上是不同的,但在实现层面,我们通常很少会这样做(定义两个UserInfo,甚至更多),因为这样做并不见得很明智。我们可以设计一个完全兼容的DTO:在服务层接受数据的时候不该由表现层设置的属性(如订单的总价应该有单价、数量、折扣等决定),无论表现层是否设置,服务层都一概忽略,而在服务层返回数据时,不该返回的数据(如用户密码),就不设置对应的属性。
之所以不在服务层 中直接返回DO的原因是:
DTO应该是一个”扁平的二维对象“,比如:如果User会关联若干个其他实体(例如Address、Account、Region等),那么getUser()返回的UserInfo,是否就需要把其关联的对象的DTO都一并返回呢?如果这样的话,必然导致数据传输量的大增,对于分布式应用来说,由于涉及数据在网络上的传输、序列化和反序列化,这种设计更不可接受。如果getUser除了要返回的User的基本信息外,还需要返回一个AccountId、AccountName、RegionId、RegionName,那么,请把这些属性定义到UserInfo中,把一个”立体“的对象树”压扁“成一个”扁平的二维对象“。
DO和PO在绝大部分情况下是一一对应的,但某些场景还是能反应出两者在概念上存在本质的区别:
由于ORM框架的功能非常强大而大行其道,而且JavaEE也退出了JPA规范,现在的业务应用开发,基本上不需要区分DO与PO,PO完全可以通过JPA,Hibernate Annotation/hbm 隐藏在DO之中。虽然如此,但有些问题我们还必须注意: