银川网站开发培训,阿里云域名注册优惠口令,淮南装饰公司网站建设,成品网站 智能建站前些天发现了一个巨牛的人工智能学习网站#xff0c;通俗易懂#xff0c;风趣幽默#xff0c;忍不住分享一下给大家。点击跳转到教程。
本篇文章主要讨论一下我们经常会用到的一些对象#xff1a;VO、DTO、DO和PO。
由于不同的项目和开发人员有不同的命名习惯#xff0c…前些天发现了一个巨牛的人工智能学习网站通俗易懂风趣幽默忍不住分享一下给大家。点击跳转到教程。
本篇文章主要讨论一下我们经常会用到的一些对象VO、DTO、DO和PO。
由于不同的项目和开发人员有不同的命名习惯这里我首先对上述的概念进行一个简单描述名字只是个标识我们重点关注其概念 概念
VOView Object视图对象用于展示层它的作用是把某个指定页面或组件的所有数据封装起来。
DTOData Transfer Object数据传输对象这个概念来源于J2EE的设计模式原来的目的是为了EJB的分布式应用提供粗粒度的数据实体以减少分布式调用的次数从而提高分布式调用的性能和降低网络负载但在这里我泛指用于展示层与服务层之间的数据传输对象。
DODomain Object领域对象就是从现实世界中抽象出来的有形或无形的业务实体。
POPersistent Object持久化对象它跟持久层通常是关系型数据库的数据结构形成一一对应的映射关系如果持久层是关系型数据库那么数据表中的每个字段或若干个就对应PO的一个或若干个属性。 模型 下面以一个时序图建立简单模型来描述上述对象在三层架构应用中的位置 l 用户发出请求可能是填写表单表单的数据在展示层被匹配为VO。
l 展示层把VO转换为服务层对应方法所要求的DTO传送给服务层。
l 服务层首先根据DTO的数据构造或重建一个DO调用DO的业务方法完成具体业务。
l 服务层把DO转换为持久层对应的PO可以使用ORM工具也可以不用调用持久层的持久化方法把PO传递给它完成持久化操作。
l 对于一个逆向操作如读取数据也是用类似的方式转换和传递略。 VO与DTO的区别 大家可能会有个疑问在笔者参与的项目中很多程序员也有相同的疑惑既然DTO是展示层与服务层之间传递数据的对象为什么还需要一个VO呢对对于绝大部分的应用场景来说DTO和VO的属性值基本是一致的而且他们通常都是POJO因此没必要多此一举但不要忘记这是实现层面的思维对于设计层面来说概念上还是应该存在VO和DTO因为两者有着本质的区别DTO代表服务层需要接收的数据和返回的数据而VO代表展示层需要显示的数据。 用一个例子来说明可能会比较容易理解例如服务层有一个getUser的方法返回一个系统用户其中有一个属性是gender(性别)对于服务层来说它只从语义上定义1-男性2-女性0-未指定而对于展示层来说它可能需要用“帅哥”代表男性用“美女”代表女性用“秘密”代表未指定。说到这里可能你还会反驳在服务层直接就返回“帅哥美女”不就行了吗对于大部分应用来说这不是问题但设想一下如果需求允许客户可以定制风格而不同风格对于“性别”的表现方式不一样又或者这个服务同时供多个客户端使用不同门户而不同的客户端对于表现层的要求有所不同那么问题就来了。再者回到设计层面上分析从职责单一原则来看服务层只负责业务与具体的表现形式无关因此它返回的DTO不应该出现与表现形式的耦合。 理论归理论这到底还是分析设计层面的思维是否在实现层面必须这样做呢一刀切的做法往往会得不偿失下面我马上会分析应用中如何做出正确的选择。 VO与DTO的应用 上面只是用了一个简单的例子来说明VO与DTO在概念上的区别本节将会告诉你如何在应用中做出正确的选择。 在以下才场景中我们可以考虑把VO与DTO二合为一注意是实现层面
l 当需求非常清晰稳定而且客户端很明确只有一个的时候没有必要把VO和DTO区分开来这时候VO可以退隐用一个DTO即可为什么是VO退隐而不是DTO回到设计层面服务层的职责依然不应该与展示层耦合所以对于前面的例子你很容易理解DTO对于“性别”来说依然不能用“帅哥美女”这个转换应该依赖于页面的脚本如JavaScript或其他机制JSTL、EL、CSS
l 即使客户端可以进行定制或者存在多个不同的客户端如果客户端能够用某种技术脚本或其他机制实现转换同样可以让VO退隐 以下场景需要优先考虑VO、DTO并存
l 上述场景的反面场景
l 因为某种技术原因比如某个框架如Flex提供自动把POJO转换为UI中某些Field时可以考虑在实现层面定义出VO这个权衡完全取决于使用框架的自动转换能力带来的开发和维护效率提升与设计多一个VO所多做的事情带来的开发和维护效率的下降之间的比对。
l 如果页面出现一个“大视图”而组成这个大视图的所有数据需要调用多个服务返回多个DTO来组装当然这同样可以通过服务层提供一次性返回一个大视图的DTO来取代但在服务层提供一个这样的方法是否合适需要在设计层面进行权衡。 DTO与DO的区别 首先是概念上的区别DTO是展示层和服务层之间的数据传输对象可以认为是两者之间的协议而DO是对现实世界各种业务角色的抽象这就引出了两者在数据上的区别例如UserInfo和User对于DTO和DO的命名规则请参见笔者前面的一篇博文对于一个getUser方法来说本质上它永远不应该返回用户的密码因此UserInfo至少比User少一个password的数据。而在领域驱动设计中正如第一篇系列文章所说DO不是简单的POJO它具有领域业务逻辑。 DTO与DO的应用 从上一节的例子中细心的读者可能会发现问题既然getUser方法返回的UserInfo不应该包含password那么就不应该存在password这个属性定义但如果同时有一个createUser的方法传入的UserInfo需要包含用户的password怎么办在设计层面展示层向服务层传递的DTO与服务层返回给展示层的DTO在概念上是不同的但在实现层面我们通常很少会这样做定义两个UserInfo甚至更多因为这样做并不见得很明智我们完全可以设计一个完全兼容的DTO在服务层接收数据的时候不该由展示层设置的属性如订单的总价应该由其单价、数量、折扣等决定无论展示层是否设置服务层都一概忽略而在服务层返回数据时不该返回的数据如用户密码就不设置对应的属性。 对于DO来说还有一点需要说明为什么不在服务层中直接返回DO呢这样可以省去DTO的编码和转换工作原因如下
l 两者在本质上的区别可能导致彼此并不一一对应一个DTO可能对应多个DO反之亦然甚至两者存在多对多的关系。
l DO具有一些不应该让展示层知道的数据
l DO具有业务方法如果直接把DO传递给展示层展示层的代码就可以绕过服务层直接调用它不应该访问的操作对于基于AOP拦截服务层来进行访问控制的机制来说这问题尤为突出而在展示层调用DO的业务方法也会因为事务的问题让事务难以控制。
l 对于某些ORM框架如Hibernate来说通常会使用“延迟加载”技术如果直接把DO暴露给展示层对于大部分情况展示层不在事务范围之内Open session in view在大部分情况下不是一种值得推崇的设计如果其尝试在Session关闭的情况下获取一个未加载的关联对象会出现运行时异常对于Hibernate来说就是LazyInitiliaztionException。
l 从设计层面来说展示层依赖于服务层服务层依赖于领域层如果把DO暴露出去就会导致展示层直接依赖于领域层这虽然依然是单向依赖但这种跨层依赖会导致不必要的耦合。 对于DTO来说也有一点必须进行说明就是DTO应该是一个“扁平的二维对象”举个例子来说明如果User会关联若干个其他实体例如Address、Account、Region等那么getUser()返回的UserInfo是否就需要把其关联的对象的DTO都一并返回呢如果这样的话必然导致数据传输量的大增对于分布式应用来说由于涉及数据在网络上的传输、序列化和反序列化这种设计更不可接受。如果getUser除了要返回User的基本信息外还需要返回一个AccountId、AccountName、RegionId、RegionName那么请把这些属性定义到UserInfo中把一个“立体”的对象树“压扁”成一个“扁平的二维对象”笔者目前参与的项目是一个分布式系统该系统不管三七二十一把一个对象的所有关联对象都转换为相同结构的DTO对象树并返回导致性能非常的慢。 DO与PO的区别 DO和PO在绝大部分情况下是一一对应的PO是只含有get/set方法的POJO但某些场景还是能反映出两者在概念上存在本质的区别
l DO在某些场景下不需要进行显式的持久化例如利用策略模式设计的商品折扣策略会衍生出折扣策略的接口和不同折扣策略实现类这些折扣策略实现类可以算是DO但它们只驻留在静态内存不需要持久化到持久层因此这类DO是不存在对应的PO的。
l 同样的道理某些场景下PO也没有对应的DO例如老师Teacher和学生Student存在多对多的关系在关系数据库中这种关系需要表现为一个中间表也就对应有一个TeacherAndStudentPO的PO但这个PO在业务领域没有任何现实的意义它完全不能与任何DO对应上。这里要特别声明并不是所有多对多关系都没有业务含义这跟具体业务场景有关例如两个PO之间的关系会影响具体业务并且这种关系存在多种类型那么这种多对多关系也应该表现为一个DO又如“角色”与“资源”之间存在多对多关系而这种关系很明显会表现为一个DO——“权限”。
l 某些情况下为了某种持久化策略或者性能的考虑一个PO可能对应多个DO反之亦然。例如客户Customer有其联系信息Contacts这里是两个一对一关系的DO但可能出于性能的考虑极端情况权作举例为了减少数据库的连接查询操作把Customer和Contacts两个DO数据合并到一张数据表中。反过来如果一本图书Book有一个属性是封面cover但该属性是一副图片的二进制数据而某些查询操作不希望把cover一并加载从而减轻磁盘IO开销同时假设ORM框架不支持属性级别的延迟加载那么就需要考虑把cover独立到一张数据表中去这样就形成一个DO对应对个PO的情况。
l PO的某些属性值对于DO没有任何意义这些属性值可能是为了解决某些持久化策略而存在的数据例如为了实现“乐观锁”PO存在一个version的属性这个version对于DO来说是没有任何业务意义的它不应该在DO中存在。同理DO中也可能存在不需要持久化的属性。 DO与PO的应用 由于ORM框架的功能非常强大而大行其道而且JavaEE也推出了JPA规范现在的业务应用开发基本上不需要区分DO与POPO完全可以通过JPAHibernate Annotations/hbm隐藏在DO之中。虽然如此但有些问题我们还必须注意
l 对于DO中不需要持久化的属性需要通过ORM显式的声明如在JPA中可以利用Transient声明。
l 对于PO中为了某种持久化策略而存在的属性例如version由于DO、PO合并了必须在DO中声明但由于这个属性对DO是没有任何业务意义的需要让该属性对外隐藏起来最常见的做法是把该属性的get/set方法私有化甚至不提供get/set方法但对于Hibernate来说这需要特别注意由于Hibernate从数据库读取数据转换为DO时是利用反射机制先调用DO的空参数构造函数构造DO实例然后再利用JavaBean的规范反射出set方法来为每个属性设值如果不显式声明set方法或把set方法设置为private都会导致Hibernate无法初始化DO从而出现运行时异常可行的做法是把属性的set方法设置为protected。
l 对于一个DO对应多个PO或者一个PO对应多个DO的场景以及属性级别的延迟加载Hibernate都提供了很好的支持请参考Hibnate的相关资料。 到目前为止相信大家都已经比较清晰的了解VO、DTO、DO、PO的概念、区别和实际应用了。通过上面的详细分析我们还可以总结出一个原则分析设计层面和实现层面完全是两个独立的层面即使实现层面通过某种技术手段可以把两个完全独立的概念合二为一在分析设计层面我们仍然至少在头脑中需要把概念上独立的东西清晰的区分开来这个原则对于做好分析设计非常重要工具越先进往往会让我们越麻木。第一篇系列博文抛砖引玉大唱领域驱动设计的优势但其实领域驱动设计在现实环境中还是有种种的限制需要选择性的使用正如我在《田七的智慧》博文中提到我们不能永远的理想化的去选择所谓“最好的设计”在必要的情况下我们还是要敢于放弃因为最合适的设计才是最好的设计。本来系列中的第二篇博文应该是讨论领取驱动设计的限制和如何选择性的使用但请原谅我的疏忽下一篇系列博文会把这个主题补上敬请关注。 原文信息如下
地址https://www.cnblogs.com/qixuejia/p/4390086.html
作者Cat Qi 出处http://qixuejia.cnblogs.com/ 本文版权归作者和博客园共有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文连接否则保留追究法律责任的权利。