论坛首页 Java企业应用论坛

炒PO-VO-DTO的冷饭,Remote下PO的运用方式问题

浏览 25353 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-11  
现在,大量使用ORM的Web应用中比较流行的做法是----直接传递PO到View中显示.

这么做的好处非常明显,再也没有复杂的VO,DTO----这些东西怎么看都很有重复代码的嫌疑.如果只有一个Object当然要直观得多.没有了那些东西,自然也就不需要进行费劲的转换(如果没有合适的工具的话,那这就是一个体力活),当然也就简单了.

这通常没有什么问题.因为,大多数情况下,Web容器和Persistence代码都在一个JVM中运行,可以通过ThreadLocal获取 Session,这样,可以LazyLoad那些关联的属性.直至生成HTML的字符串,PO对象庞大的ObjectGraph始终都在同一个JVM中运行,没有传输和远程,一切都是那么美好.

可是----咱们不是马上就要奔RIA了吗,也就是说,Client端现在Rich了,那么,新环境,会不会有新问题呢?

答案是否定的. ;)

RIA的环境下.

1. 客户端是有脑的,也就是说,你不能象Browser一样,扔给我一个HTML字符串就了事,现在我要动用我的"计算能力"了,你至少要给我几个Object才象话吧.
2. 通讯是有代价的,那就是说,如果我要一个小的东西,你也照样扔给我一大堆(ObjectGraph),虽说也可以跑,但是要在网络上传输这样的大家伙,可不要指望能我跑得快.

简而言之,也就是说,在RIA环境,服务器递过来的,应该是大小合理而又功能完备的Object.请注意,应该是小的Object,而不是大的ObjectGraph.

换个角度,如果把Web应用的View层看作Persistence代码的一个Client话,那么,一般的Web应用中,这个Client是Local的.而在RIA环境下,这个Client要Remote了.

于是,经典的"反模式"----DTO粉末登场了(它不就是为了远程传输而出现的么).

我们真的必须要这样做吗?

......

各位,Any Suggestion?

(思考,笔记于 http://www.jroller.com/page/jackyz 略有删节)

ps. 翻看旧贴 http://forum.iteye.com/viewtopic.php?t=5282
potian 写道
其实越复杂的系统就越难使用DTO,有时候甚至我们宁可承担远程对象的开销都愿意直接把领域对象传输到界面上。

......深有共鸣.
   发表时间:2005-03-11  
这次开发JavaEye门户网站我和Quake Wang讨论了一下,不准备使用OpenSessionInView,就是为了将来可能的远程访问提供服务。确实,考虑到远程访问的情况下,编程模型会稍有不同,但是也不会很大,其实无非就是把lazy的持久对象fetch从持久对象的方法中挪到了Dao接口方法而已,并不会增加什么复杂度,DTO也没有必要引入。
0 请登录后投票
   发表时间:2005-03-11  
robbin 写道
这次开发JavaEye门户网站我和Quake Wang讨论了一下,不准备使用OpenSessionInView,就是为了将来可能的远程访问提供服务。确实,考虑到远程访问的情况下,编程模型会稍有不同,但是也不会很大,其实无非就是把lazy的持久对象fetch从持久对象的方法中挪到了Dao接口方法而已,并不会增加什么复杂度,DTO也没有必要引入。


你的意思是不是准备用 DAO 来预先 Fetch PO 可能用到的 Lazy 的属性.然后再将它传递给 Remote 的 Client 程序?

如果是这样,那么:

1. 在无法预期 RemoteClient 将如何"使用"这个 PO 的假设下,如何能够避免它去访问"没有预先Fetch的属性"呢?

2. 针对不同的"视图",比如: List 和 Detail ,是否用同样的"预 Fetch 逻辑"得到的 PO ? 如果用同样的,那么对于 List 这个 PO 可能过大,传输效率不高. 如果用不同,那么除了"硬编码"之外,这两个处理过程的 Client 端代码是否有办法区分当前的 PO 适用于 List 还是 Detail?

总觉得"预 Fetch"的方式有些"私下约定"的味道.
0 请登录后投票
   发表时间:2005-03-11  
一般的PO也就直接传输给客户端了,many-to-one和one-to-one的情况也可以直接传输给客户端了,毕竟就是多传输一个对象而已,唯一需要特殊处理的地方是one-to-many的情况,不能够在传输一个对象的同时,把many那一端的n个对象一起传输,这样网络通讯量太大,速度太慢,这种情况下在Hibernate端使用lazy="true",只fetch一个one端的持久对象到客户端。当客户端需要fetch这个对象的关联对象集合的时候,再以这个对象做为参数,远程调用服务器端方法,获取关联对象集合。
0 请登录后投票
   发表时间:2005-03-12  
robbin 写道
一般的PO也就直接传输给客户端了,many-to-one和one-to-one的情况也可以直接传输给客户端了,毕竟就是多传输一个对象而已,唯一需要特殊处理的地方是one-to-many的情况,不能够在传输一个对象的同时,把many那一端的n个对象一起传输,这样网络通讯量太大,速度太慢,这种情况下在Hibernate端使用lazy="true",只fetch一个one端的持久对象到客户端。当客户端需要fetch这个对象的关联对象集合的时候,再以这个对象做为参数,远程调用服务器端方法,获取关联对象集合。


一般的PO可以直接传给Client,这点大家都没有异议.带关联对象的PO是否能直接传给Client呢?好像需要考虑一下.

你的做法是将所有one-to-many的属性去掉.那么,PO中只剩下many-to-one和one-to-one的属性.传递的时候,即使多传了一些对象,数量也有限的.这似乎是个不错的办法.但,有几个状况,在这种方式下还是值得疑问的.

1. 树结构.

每个节点的parent都能满足many-to-one的规则.那么,在传输的时候,是否有必要每次都上溯到Root节点呢?这样做似乎很浪费.

2. 在对象的依赖关系层次上比较底层的对象.

即,类似(A->B表示A依赖B,比如存在A.getB()的方法):
A->B->F->G
A->C->H
A->D->I
A->E
类似于A这样的"底层"对象在我们的系统中可能很常见,它们通常是一些"业务数据",需要依赖一些"数据字典"或者"参考值"一类的东西.

比如合同要依赖:客户,用户,业务地区等.如果再考虑到某个被依赖的对象存在树结构(比如业务地区是树结构)的情况.那么,一个业务对象的传递可能必须会要带上一大堆这样的关联对象.而,实际上,这些被对象往往是比较"固定"的,如果每次都传输它们,效率上似乎是有问题的.



我有个想法,就是,能否将Hibernate的LazyLoad机制"延伸"到Client端,通过在Client端建立类似Session的机制(或者模拟它),从而使带有LazyLoad的PO对象能够在Client端很好的运作,同时又具有大小合适的"传输代价".

这似乎应该从Hibernate的CGLIB Enhance开始...对这个Topic,谁有经验?
0 请登录后投票
   发表时间:2005-03-12  
我不觉得你说的这种情况是一个问题,就我的经验而言,也许你可以试试去掉OpenSessionInView做一个项目看看就知道了。

引用
我有个想法,就是,能否将Hibernate的LazyLoad机制"延伸"到Client端,通过在Client端建立类似Session的机制(或者模拟它),从而使带有LazyLoad的PO对象能够在Client端很好的运作,同时又具有大小合适的"传输代价".


基本上不可以,这将涉及到透明的远程方法调用。而如果要实现透明的远程方法调用的话,必须有一个容器的支持,而不可能由Hibernate本身来完成。传统的CMP是一个透明的lazyload方案,符合你大部分要求,但是调用颗粒度太细,网络通讯过于频繁,所谓合适的传输代价和颗粒度,我认为我提出的方案是最适中的。
0 请登录后投票
   发表时间:2005-03-13  
potian 写道

其实越复杂的系统就越难使用DTO,有时候甚至我们宁可承担远程对象的开销都愿意直接把领域对象传输到界面上。

就我的经验来看恰恰相反,越复杂的系统DTO越好用!理由如下:
复杂的系统意味着复杂的DomainObject的结构。后台人员开发出的复杂DomainObject结构,没有必要让负责开发前端应用层伙伴知道。这是一种封装,《人月神话》的作者二十年后,也改变了自己的想法,赞同这种封装的好处。确实,没有必要让开发人员了解系统的每一处结构。

另外让我们看看前台开发人员,他拿着一把奥卡姆剃刀,对于约定前后台行为的接口说:“把对我来说没用到的东西去掉”。“客户就是上帝”,使用服务接口的
开发人员有这个权利。

对于“获取客户基本信息”这样一个方法来说,前台开发人员想得到的仅仅是一个对象,它包含了客户的代码、姓名、身份证号码、客户状态(冻结|挂失|注销)。
如果采用直接传递DomainObject的方式,后台人员会告诉他“在客户这个对象中没有客户状态,你需要分别找到相关的协议,然后组装他们。另外不好意思,本来你现在只需要客户对象的三个属性,但是我把客户的所有属性(大概几十个)都传给你了,你别介意,这也是为了你将来好用考虑,说不定那天你会需要的哩。谁能说得定呢?”
前台开发人员:“NND,这比商场过年买东西还划算,买一送十啊,而且是全自动”

复杂的系统通常需要并行开发,让前台开发人员等待后台完成DomainObject才能使用是没有道理的。而且这样也会导致后台DomainObject的变化影响前台的工作。想反通过事先定义的服务接口和VO可以使这种依赖减小到最低,仅限于接口部分。

robbin 写道
一般的PO也就直接传输给客户端了,many-to-one和one-to-one的情况也可以直接传输给客户端了,毕竟就是多传输一个对象而已,唯一需要特殊处理的地方是one-to-many的情况,不能够在传输一个对象的同时,把many那一端的n个对象一起传输,这样网络通讯量太大,速度太慢,这种情况下在Hibernate端使用lazy="true",只fetch一个one端的持久对象到客户端。当客户端需要fetch这个对象的关联对象集合的时候,再以这个对象做为参数,远程调用服务器端方法,获取关联对象集合。

不是说业务层是最底层的东西吗?为什么要在这里考虑应用层和分布式的情况下的问题呢?
既然业务层是最底层的东西,就该是其他层考虑它的存在,而不是相反。
0 请登录后投票
   发表时间:2005-03-13  
partech 写道


另外让我们看看前台开发人员,他拿着一把奥卡姆剃刀,对于约定前后台行为的接口说:“把对我来说没用到的东西去掉”。“客户就是上帝”,使用服务接口的
开发人员有这个权利。

控制不好的后果就是dto爆炸,当dto的一些字段发生变动时,就是爆炸的时候。这对采用xp方式进行开发的小组是很困难的事。
0 请登录后投票
   发表时间:2005-03-14  
partech 写道
复杂的系统意味着复杂的DomainObject的结构。后台人员开发出的复杂DomainObject结构,没有必要让负责开发前端应用层伙伴知道。这是一种封装,《人月神话》的作者二十年后,也改变了自己的想法,赞同这种封装的好处。确实,没有必要让开发人员了解系统的每一处结构。


再仔细思考了一遍,为什么我希望在Client使用PO(或者DomainObject)?我之所以希望能够在Client端有DomainObject,主要是因为——我自己既写后端代码又写前端代码——对于我来说,在Client端直接使用DomainObject,不仅功能丰富,而且无需处理和DomainObject“高度类似”的DTO的生成和转换的问题(DTO代码怎么着都让人有种和DomainObject重复的感觉)。

partech说得也有道理,当前后端代码并非我一个人来写的时候,缺少DTO也就意味着缺少了一个合理的隔离层次。必然导致前端代码要依赖于后端代码,前后端代码的紧密结合,使得前后端的并行开发变得困难。

我想这个问题和项目规模也是密切相关的,对小项目也许合理(人数少的话,所有人都了解DomainObject似乎也是可行),对大项目则可能是不可接受的。就目前来看,如果不能找到合理的办法解决传输代价,目前也只能使用DTO方式,并无其他选择。


robbin 写道
基本上不可以,这将涉及到透明的远程方法调用。而如果要实现透明的远程方法调用的话,必须有一个容器的支持,而不可能由Hibernate本身来完成。传统的CMP是一个透明的lazyload方案,符合你大部分要求,但是调用颗粒度太细,网络通讯过于频繁,所谓合适的传输代价和颗粒度,我认为我提出的方案是最适中的。


查过了文档,现在的Hibernate确实是不可能提供这个特性。

但,就概念而言,也许并非没有实现的可能。无需重新发明一个ORM,也许可以从Hibernate的Enhance过程入手,Hack它的LazyLoad实现。比如,在服务器端LazyLoad的时候,就Lookup Hibernate 本地的原生 Session 实例,在客户端 LazyLoad 的时候,就 Lookup Hibernate 在本地的一个 Session Implement (也就是引入一个Factory而已),这个 Implement 会调用远程 Session 服务的包装接口,获取所需的对象(这个过程对于外部代码会是透明的)。如果加上合理的 Cache 机制,效果应该不会很差。这里的众位高手,如果有时间和兴趣的,可以试试。

Google了一下,很凑巧,TSS上也有人有过类似的想法。
http://www.theserverside.com/patterns/thread.tss?thread_id=17886
0 请登录后投票
   发表时间:2005-03-14  
赞同partech的看法.
定义好前台与业务层之间的接口以及DTO之后,前台和后台就可以并行的开发了.
然后通过频繁的集成'磨合;前台后台.
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics