ValueObject和DTO模式的一些疑问

ValueObject和DTO模式是常用的J2EE设计模式,模式所体现的不仅仅是一个设计技巧,更主要的是数据封装的思想。最近我在做设计的时候却对这个模式产生了一些疑问。
1. 对于位于持久层中的ValueObject,我是否应该限制他们的使用范围?以Hiberante为例,由于ValueObject的生命周期的特性,我们不能把他们当作DTO在层间传递。但是,如果使用BeanUtils等工具复制一个的话,却总是感觉有些浪费内存。如果是EntityBean则没有这些疑问――很显然我不能把EJB Bean实例当作DTO在层间传递――浪费就浪费吧。
2. 表现层的表单也需要对象封装,在Struts中就是FormBean,但是在JSF中却没有这样的概念,这样一不小心我们就容易在表现层直接使用model中的对象,这也是不合理的。
3. Business层需要处理从表现层传入的数据对象,这个数据对象也不能是FormBean或其他表现层VO,因为往往这些表现层VO直接反映的是表单数据,例如一个String格式的日期或者confirmedPassword,前者需要转化为Date类型而后者则在Business层用不到,这些处理也应该在表现层处理(?)。
4. DAO层接收Business层的数据,这个数据应是Business层对表现层数据的处理结果。
这样一来,我们在处理一个case的时候就出现了3个ValueObject:
 一个是表现层的用于反映页面数据。
 一个是Business层的,用于作为表现层和Business之间的DTO。
 另一个是Domain&DAO层的,用于持久化的ValueObject。
这真是浪费呀!
我的疑问是,我是否可以省略其中的一个或多个?还是根据不同情况和需求灵活处理,各位大虾是如何做的?

我一般是用一个来传递的,我主要是觉得封装太麻烦,包来拆去的。
你可以参考一下我以前的一篇贴子,好像是jsp 传2维数组。
这样做的缺点就是和面向对象的思想有些脱离,及每个对象,对业务的反映能力不强。
其实,这也是架构时常用的一种"模糊"技巧。只有模糊了,才能既是a 也是b,才能重用。具体实现中,我的手段主要是,接口,参数,弹性域,当然向上还有框架。

DTO模式就是ValueObject模式,DTO是后来更名的,这在TSS首页的EJB模式或SUN的J2EE核心模式中有过说明。

>DTO模式就是ValueObject模式,DTO是后来更名的,这在TSS首页的EJB模式或SUN的J2EE核心模式中有过说明。
这些说明也不一定准确,值对象模式主要考虑的是数据封装,而数据传送对象则考虑数据层间传递的方法,二者虽然外表相同,但是考虑问题的角度却不同。例如在同一个层中,就没有DTO之说了。
但是这些区别并不重要,我的问题是DTO在不同层的属性是不同的,尤其是在以Hibernate为核心的DAO层,VO和PO是不能作为DTO使用的(?)。而表现层的Form数据和业务层所需要的数据也不同。对于一个UseCase而言,是否需要为每一个层都建立一个数据对象呢?

〉DTO在不同层的属性是不同的

DTO应该主要是指相邻层的层间传递的,因此只要把相邻的两层间的问题设计好了就行,至于,不同层的属性不同,那不应该属于DTO的问题。

我觉得你的问题提到点子上了,持久层和界面层从两边向中间拱,甚至每个层建立一个数据对象,最后导致中间层混乱,整个系统反而变得难以维护,这是目前一些J2EE系统的通病,正是这种现象使得干净清爽的naked Object域对象模型受到欢迎。如下图,左图是现在混乱的J2EE多层系统,右图使用Naked Object的后的模型

实际上,不一定所有的系统都一定要使用Naked Object,而且Naked Object导致耦合性高,目前实验只适合在Swing界面端,在服务器端成功案例还没有出现。

这给我们一个启示,我们做一个J2EE系统时,一开始必须从中间层Domain Model开始,这就是域模型驱动,而不是被具体技术牵着鼻子走,各个层技术就象小孩子一样,闹腾厉害,如果你给他们每个人分一个果果(做一个数据模型),那么你整合起来时很难,所以,你必须只给他们一个Domain Model果果,以此设计为中心,再加入其他辅助对象,与Domain Model建立对象关联(通过类图实现的四种类关系),而各个技术都是围绕类图的类各自完成自己功能。

理清这个思路,从中间组件层入手,就能顺藤摸瓜,问题迎刃而解,这也是为什么组件层(有些人说是构件)如此重要,为什么要面向组件(面向构件)编程的重要性,为什么EJB和Spring等等吵得不可开交等原因。

实际上,也是我提出为什么域建模 模式 和框架如此重要的原因,使用UML依靠usecase设计出类图、用模式实现类图、使用框架(表现层 组件层 持久层框架)来进行代码级别实现,最终完成一个高质量的J2EE系统。

是呀,我也觉得不属于DTO的问题,应该是“关于”DTO的问题吧。但是,我就是想知道,是否各个层都需要各自的DTO。

> 是呀,我也觉得不属于DTO的问题,应该是“关于”DTO的问题
> 但是,我就是想知道,是否各个层都需要各自的DTO。

8需要。hibernat持久层和业务层虽然用不同的object,但是是同样的domain model,完全不用担心new object()的开销,和持久层的远程调用比起来,这点耗费可以忽略不计
另外,我认为大部分的domain model完全可以传到展现层,使用FormBean有时候反而会造成混乱

>domain model完全可以传到展现层,使用FormBean有时候反而会造成混乱

非常正确,在我的Jdon框架里,我推荐FormBean是Model的完全拷贝,除非你在界面有一些只与界面相关的小功能,可以增加FormBean的一些字段。有的系统使用FormBean中调用Model(就是Model是FormBean的字段变量)的做法(如IBatis JPetstore),这实际将两者变成了一种调用关系,一旦成为关系,就打开潘多拉盒子,变得复杂或者混乱,我不喜欢这样使用。

你也太死板了吧,完全被pattern给套死了。在一般的应用中persistent object传导表现层又如何?何必再来个copy的无用功?即使用hibernate,在一个层次划分比较明显的系统里,难道view层的开发人员能够获取到hibernate的session从而修改你的逻辑吗?至于formbean,对于我来说只是利用到validation比较结构清晰,否则的话我宁愿request.getparameter来的直接。一般来说在business layer设计好接口后,都是按照business的接口需要来取request的数据,何必把business layer的设计邦定到formbean的设计上呢

哈哈 dabb好久不见了,很高兴看到你,我担心persistent object直接传到presentation layer,会造成这两个层耦合,这样,在这两者之间插一个第三者Model,就断了他们藕断丝连的想头了。

>domain model完全可以传到展现层,使用FormBean有时候反而会造成混乱
这在使用DTO + EntityBean + DTO Assembler的情况下没有问题,因为DTO和EntityBean是游离的。DTO在传送过程中发生的变化不会影响EntityBean。这也算EntityBean的优点吧――强迫你使用DTO。
但是,对于使用Hibernate的轻量级DAO层来说却容易引发问题,因为你不能保证“那个坐在角落里的实习生”也和你一样注意实体的状态,一旦处于PO状态的实体被传送到其他层,那么就有可能对数据产生意想不到的破坏。所以好的方法是仍然使用DTOAssembler模式,其作用就是产生一个新的Object,其内容与Model层的内容相同,但是他的数据不会被持久化,它是完全的DTO。原来这是一个非常麻烦的工作,因为你需要写很多set/get,现在可以使用BeanUtil就方便多了。
在Hibernate的情况下,DTOAssembler其实相当于DAO层和Business层的解耦器。

现在明白了一些,对于“Model类”而言,持久层和Business都可以使用Domain中的“类”,而不必分开,正如banq所言,以model为中心。

那么表现层呢,你可以把Model中一个String类型的字段对应一个Text表单,但是其他类型呢?yuxie说“大部分的domain model”,这个大部分“大”到什么程度是很重要的。有没有更好的解决方法呢?

〉难道view层的开发人员能够获取到hibernate的session从而修改你的逻辑吗?
不是没有可能,即便如此,虽然view层的无法获得session,但是他可以任意修改persistence层的对象也是很危险的,persistence层是不会对数据进行验证的,它只会不管3721的保存数据。

我们应该确保各层之间认知程度最低。

>会造成这两个层耦合
不仅如此,直接在表现层修改Persistence层的数据的危险性还在于:破坏了Domain的独立性。DomainModel的实例s构成了逻辑上Domain层,它相当于数据库在内存中的映射,在使用ORM的情况下更是如此。
在表现层修改Persistence层的数据相当于破坏了这种映射,其后果是十分严重的。
例如,表现层代码user.setUsername("CatsTiger");
看上去没有问题,但是如果User对象处于Persistent状态就麻烦了,等于没有经过Business层处理就保存了数据,回到C/S时代了。这有点像把EntityBean作为DTO了:(

> ValueObject和DTO模式是常用的J2EE设计模式,模式所体现的
> 唤鼋鍪且桓錾杓萍记桑饕氖鞘莘庾暗乃枷搿W罱以
> 做设计的时候却对这个模式产生了一些疑问。
> 1. 对于位于持久层中的ValueObject,我是否应该限制他们?> 使用范围?以Hiberante为例,由于ValueObject的生命周期的
> 匦裕颐遣荒馨阉堑弊DTO在层间传递。但是,如果使用Be
> nUtils等工具复制一个的话,却总是感觉有些浪费内存。如果
> EntityBean则没有这些疑问――很显然我不能把EJB
> Bean实例当作DTO在层间传递――浪费就浪费吧。
> 2. 表现层的表单也需要对象封装,在Struts中就是FormBean
> 窃JSF中却没有这样的概念,这样一不小心我们就容易在
> 硐植阒苯邮褂model中的对象,这也是不合理的。
> 3. Business层需要处理从表现层传入的数据对象,这个数据
> 韵笠膊荒苁FormBean或其他表现层VO,因为往往这些表现层V
> 直接反映的是表单数据,例如一个String格式的日期或者conf
> rmedPassword,前者需要转化为Date类型而后者则在Business
> 阌貌坏剑庑┐硪灿Ω迷诒硐植愦恚ǎ浚?> 4. DAO层接收Business层的数据,这个数据应是Business层?> 表现层数据的处理结果。
> 这样一来,我们在处理一个case的时候就出现了3个ValueObje
> t:
>  一个是表现层的用于反映页面数据。
>  一个是Business层的,用于作为表现层和Business?> 间的DTO。
>  另一个是Domain&DAO层的,用于持久化的ValueObje
> t。
> 这真是浪费呀!
> 我的疑问是,我是否可以省略其中的一个或多个?还是根据不
> 榭龊托枨罅榛畲恚魑淮笙菏侨绾巫龅模?


JSF中的Model是它自己的称法吧.我理解为就是vo.而类型转换也是JSF要处理的东西,而JSF struts ww这种框架,可以理解为广义上的表现层,所以这些可以说是要表现层处理的。

而每层之间是否需要一个独立的DTO来传递,根据项目复杂度灵活处理。
小项目po或者formbean透传又有何不可呢?