锁定老帖子 主题:为什么我的程序传递DTO
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-03-18
我想先从交互过程来看看:
第一阶段:从外部获得数据,外部数据被组装成一定的格式. 第二阶段:使用第一阶段获得的数据,调用与该交互相关的业务处理方法 第三阶段:将响应数据交予表现层. 第一阶段数据应该被组装成怎样的格式?第二阶段,传递给业务处理方法的数据格式?第三阶段,响应数据的格式如何设计? 在第一阶段的数据格式容易,目标可只定为能解析出来. 在第二阶段,传递给业务处理方法的数据格式,由业务层的设计来决定,怎样的设计是为之好!? 在第三阶段,返回数据应该能体现出业务意义. 在这几个阶段,DTO或是PO的影子在那里? 考虑一个例子: 用户创建一个新的雇员 一,业务方法的设计 业务处理方法的责职假设为检查新雇员的相关属性是否合乎要求,改变雇员内部属性,持久化. 单从这个例子的责职来看,employee.create()或是employeeManager.create(employee)都是合理的. 二,外部输入格式的设计 是否在第一阶段,外部数据设计格式符合业务实体的模型(这样就可以利用BeanUtils之类工具来很快创建一个实体模型,如employee.post.department之类的输入格式)? 使用employee.post.department的输入形式耦合了业务实体的设计?这跟具体的解析方法相关,比如用BeanUtils这样的工具,那么耦合. 还是设计成emplyeeId,departmentId,不耦合,无论怎么变,都ok. 三,响应格式 大概同上,不废话. 说了那么多,DTO,PO这两个概念乱,想理清下自己的思维. |
|
返回顶楼 | |
发表时间:2006-03-18
nihongye 写道 我想先从交互过程来看看:
第一阶段:从外部获得数据,外部数据被组装成一定的格式. 第二阶段:使用第一阶段获得的数据,调用与该交互相关的业务处理方法 第三阶段:将响应数据交予表现层. 第一阶段数据应该被组装成怎样的格式?第二阶段,传递给业务处理方法的数据格式?第三阶段,响应数据的格式如何设计? 在第一阶段的数据格式容易,目标可只定为能解析出来. 在第二阶段,传递给业务处理方法的数据格式,由业务层的设计来决定,怎样的设计是为之好!? 在第三阶段,返回数据应该能体现出业务意义. 在这几个阶段,DTO或是PO的影子在那里? 考虑一个例子: 用户创建一个新的雇员 一,业务方法的设计 业务处理方法的责职假设为检查新雇员的相关属性是否合乎要求,改变雇员内部属性,持久化. 单从这个例子的责职来看,employee.create()或是employeeManager.create(employee)都是合理的. 二,外部输入格式的设计 是否在第一阶段,外部数据设计格式符合业务实体的模型(这样就可以利用BeanUtils之类工具来很快创建一个实体模型,如employee.post.department之类的输入格式)? 使用employee.post.department的输入形式耦合了业务实体的设计?这跟具体的解析方法相关,比如用BeanUtils这样的工具,那么耦合. 还是设计成emplyeeId,departmentId,不耦合,无论怎么变,都ok. 三,响应格式 大概同上,不废话. 说了那么多,DTO,PO这两个概念乱,想理清下自己的思维. DTO的引入虽然减少了层之间的耦合,但是引入得复杂度是很难让我接受的。 Assembler/Deassember的动作相当繁琐。 其实页面数据到领域模型数据之间的相互转换是避免不了的,如果把这个动作理解为DTO的一个根本指责的话,那么DTO的引入的必要性就很值得斟酌,在一个复杂的非WEB项目中, 这样的中间转换的过程很有必要提高的一个整体的架构设计的一部分,作为项目初期设计的必要攻克部分。 我先说我的结论,对于复杂C/s结构: 如果clien层志依赖于平面的文本数据,那么整体架构俄更加简洁和清晰。 对于clien,可以基于任意的数据格式进行展现和回应修改的数据。因为这些数据是平面的数据,设计人员可以任意设计。 对于Server段,assember/deassember这些平面数据可以抽取到一个中间架构来,这个架构可以很类似Webwork,也可以自己做一个简洁的Controller+数据转换层。 数据转换层可以参考webwork的ognl部分。 很重要的一个逻辑例子: 领域模型: A --->B----> C 视图View: field0:A.id field1:A.name field2: A.B.name field3:A.B.C.name 转换成平面数据: <request> <data><field name="A.id">23424</field><field name="A.B.name">haha</field> ...... </data> <service package="someServiceGroup" id="testService" /> </request> 这样的一个数据载体类似于web 的paramer map. Controller作主要的服务定位 service: package.id -->someServiceGroup.testService 而对于数据依赖于ognl作实现。 |
|
返回顶楼 | |
发表时间:2006-03-18
yimlin 写道 不过写太多的assembler代码也很郁闷的,希望有一种类似hibernate的东西来处理,尝试中。
赫赫,你这个建议怕是要吓跑很多人。 一个hibernate就够花时间学习了,难道还需要第二个? 就我看来,assembler的转换工作比起hibernate的OR来说,百分之一吧。 没有metadata,cache,indentitymap,hql。转化代码确实很简单。 |
|
返回顶楼 | |
发表时间:2006-03-18
partech 写道 yimlin 写道 不过写太多的assembler代码也很郁闷的,希望有一种类似hibernate的东西来处理,尝试中。
赫赫,你这个建议怕是要吓跑很多人。 一个hibernate就够花时间学习了,难道还需要第二个? 就我看来,assembler的转换工作比起hibernate的OR来说,百分之一吧。 没有metadata,cache,indentitymap,hql。转化代码确实很简单。 或许这个转换的工作逻辑清除,形势单一,所以你不认为它繁琐吧,因为在你们的家沟设计中,这种转换是合情合理 而且必要的,但是不可否认的是,这种代码是重复而繁琐的 。 |
|
返回顶楼 | |
发表时间:2006-03-18
firebody 写道 partech 写道 yimlin 写道 不过写太多的assembler代码也很郁闷的,希望有一种类似hibernate的东西来处理,尝试中。
赫赫,你这个建议怕是要吓跑很多人。 一个hibernate就够花时间学习了,难道还需要第二个? 就我看来,assembler的转换工作比起hibernate的OR来说,百分之一吧。 没有metadata,cache,indentitymap,hql。转化代码确实很简单。 或许这个转换的工作逻辑清除,形势单一,所以你不认为它繁琐吧,因为在你们的家沟设计中,这种转换是合情合理 而且必要的,但是不可否认的是,这种代码是重复而繁琐的 。 看看下面的示例,TypeInfo通常会出现在下拉的Combo中,这样的场景包含了我们DomainObject到DTO转化,assembler的典型代码,整个系统做完了,也没看见过比这复杂的代码。 对于DTO到DomainObject的转化需要通过接口来实现,因为DomainObject不能直接调用Assembler的代码,通过Assembler的不同构造函数来适配不同DTO: public class TypeInfo{ //ID public ID id; // 代码 public String code; // 名称 public String name; public TypeInfo(ID ID,String code,String name);{ this.id = ID; this.code = code; this.name = name; } } public class TypeInfoAssembler{ public static TypeInfo create(SimpleDomainObject simpleDomainObject);{ return new TypeInfo(simpleDomainObject.getID();, simpleDomainObject.getCode();, simpleDomainObject.getName(););; } private static TypeInfo create(Security security);{ return new TypeInfo(security.getID();, security.getSymbol();, security.getName(););; } public static TypeInfo create(TradingMarket tradingMarket);{ return new TypeInfo(tradingMarket.getID();, tradingMarket.getCode();, tradingMarket.getName(););; } public static TypeInfo create(SecurityCategory securityCategory);{ return new TypeInfo(securityCategory.getID();, securityCategory.getCode();, securityCategory.getName(););; } public static TypeInfo[] createArray(DomainObjectCollection domainObjectCollection);{ TypeInfo[] typeInfoList = new TypeInfo[domainObjectCollection.Count]; for(int i = 0; i < domainObjectCollection.Count; i++);{ if(domainObjectCollection[i] instanceof SimpleDomainObject);{ typeInfoList[i] = create((SimpleDomainObject);domainObjectCollection[i]);; }else if(domainObjectCollection[i] instanceof Security);{ typeInfoList[i] = create((Security);domainObjectCollection[i]);; } } return typeInfoList; } } public interface IDomainObjectAssembler{ IDomainObject create();; DomainObjectCollection createCollection();; } |
|
返回顶楼 | |
发表时间:2006-03-18
partech 写道 firebody 写道 partech 写道 yimlin 写道 不过写太多的assembler代码也很郁闷的,希望有一种类似hibernate的东西来处理,尝试中。
赫赫,你这个建议怕是要吓跑很多人。 一个hibernate就够花时间学习了,难道还需要第二个? 就我看来,assembler的转换工作比起hibernate的OR来说,百分之一吧。 没有metadata,cache,indentitymap,hql。转化代码确实很简单。 或许这个转换的工作逻辑清除,形势单一,所以你不认为它繁琐吧,因为在你们的家沟设计中,这种转换是合情合理 而且必要的,但是不可否认的是,这种代码是重复而繁琐的 。 看看下面的示例,TypeInfo通常会出现在下拉的Combo中,这样的场景包含了我们DomainObject到DTO转化,assembler的典型代码,整个系统做完了,也没看见过比这复杂的代码。 对于DTO到DomainObject的转化需要通过接口来实现,因为DomainObject不能直接调用Assembler的代码,通过Assembler的不同构造函数来适配不同DTO: public class TypeInfo{ //ID public ID id; // 代码 public String code; // 名称 public String name; public TypeInfo(ID ID,String code,String name);{ this.id = ID; this.code = code; this.name = name; } } public class TypeInfoAssembler{ public static TypeInfo create(SimpleDomainObject simpleDomainObject);{ return new TypeInfo(simpleDomainObject.getID();, simpleDomainObject.getCode();, simpleDomainObject.getName(););; } private static TypeInfo create(Security security);{ return new TypeInfo(security.getID();, security.getSymbol();, security.getName(););; } public static TypeInfo create(TradingMarket tradingMarket);{ return new TypeInfo(tradingMarket.getID();, tradingMarket.getCode();, tradingMarket.getName(););; } public static TypeInfo create(SecurityCategory securityCategory);{ return new TypeInfo(securityCategory.getID();, securityCategory.getCode();, securityCategory.getName(););; } public static TypeInfo[] createArray(DomainObjectCollection domainObjectCollection);{ TypeInfo[] typeInfoList = new TypeInfo[domainObjectCollection.Count]; for(int i = 0; i < domainObjectCollection.Count; i++);{ if(domainObjectCollection[i] instanceof SimpleDomainObject);{ typeInfoList[i] = create((SimpleDomainObject);domainObjectCollection[i]);; }else if(domainObjectCollection[i] instanceof Security);{ typeInfoList[i] = create((Security);domainObjectCollection[i]);; } } return typeInfoList; } } public interface IDomainObjectAssembler{ IDomainObject create();; DomainObjectCollection createCollection();; } 先不说这段代码,先说DTO的角色吧,是不是意味着每一个view都对应一个DTO呢? 如果有这么一个视图确确实实是编辑单个实体的属性,那么你们是不是还是要一个单独的DTO来折衷你们整体的架构设计呢? 还是根据POJO要组合彩需要DTO的出现呢? 如果是前者,我不得不怀疑又出现两套几乎相同field的对象的出现。 如果是后者,那设计者就似乎肯定了一点,尽量避免DTO的出现。 |
|
返回顶楼 | |
发表时间:2006-03-18
firebody 写道 先不说这段代码,先说DTO的角色吧,是不是意味着每一个view都对应一个DTO呢?
如果有这么一个视图确确实实是编辑单个实体的属性,那么你们是不是还是要一个单独的DTO来折衷你们整体的架构设计呢? 还是根据POJO要组合彩需要DTO的出现呢? 如果是前者,我不得不怀疑又出现两套几乎相同field的对象的出现。 如果是后者,那设计者就似乎肯定了一点,尽量避免DTO的出现。 1。View同DTO是多对多的关系。常用的场景是一个View对应多个DTO。 2。编辑单个实体,也需要一个XXXInfo来冲当DTO。没啥好折中的,DomainObject永远不允许跑出Domain层。 |
|
返回顶楼 | |
发表时间:2006-03-18
partech 写道 firebody 写道 先不说这段代码,先说DTO的角色吧,是不是意味着每一个view都对应一个DTO呢?
如果有这么一个视图确确实实是编辑单个实体的属性,那么你们是不是还是要一个单独的DTO来折衷你们整体的架构设计呢? 还是根据POJO要组合彩需要DTO的出现呢? 如果是前者,我不得不怀疑又出现两套几乎相同field的对象的出现。 如果是后者,那设计者就似乎肯定了一点,尽量避免DTO的出现。 1。View同DTO是多对多的关系。常用的场景是一个View对应多个DTO。 2。编辑单个实体,也需要一个XXXInfo来冲当DTO。没啥好折中的,DomainObject永远不允许跑出Domain层。 问题是 DomainObject不允许跑出Domain层,就一定需要使用DTO吗? 正如我前面所说的,我们也可以让View层依赖于平面的数据,比如XML等等。 让中间层作这么一个架构的设计。 而且从你提供的代码来看,仍然脱离不了重复性的代码而逻辑重复的各种Assembler 。 这种代码我认为是不大“舒服“得。 而且,为了阐明我的观点,我想引用一下你的标题: 再明确分层的前提下,一定需要DTO吗?我的答案是尽量避免使用DTO。 即使这样的结果需要一个精心设计的中间层作数据组装,我仍然觉得很有必要。 而且Controller和哪个中间层的设计完全可以参照webwork的思路。这样的一个设计如果能够消除DTO的出现,我想我会优先考虑的。 |
|
返回顶楼 | |
发表时间:2006-03-18
firebody 写道 问题是 DomainObject不允许跑出Domain层,就一定需要使用DTO吗?
正如我前面所说的,我们也可以让View层依赖于平面的数据,比如XML等等。 让中间层作这么一个架构的设计。 而且从你提供的代码来看,仍然脱离不了重复性的代码而逻辑重复的各种Assembler 。 这种代码我认为是不大“舒服“得。 而且,为了阐明我的观点,我想引用一下你的标题: 再明确分层的前提下,一定需要DTO吗?我的答案是尽量避免使用DTO。 即使这样的结果需要一个精心设计的中间层作数据组装,我仍然觉得很有必要。 而且Controller和哪个中间层的设计完全可以参照webwork的思路。这样的一个设计如果能够消除DTO的出现,我想我会优先考虑的。 "尽量避免DTO"?看起来DTO就像“瘟疫”一样需要躲避。 如果你认为平面格式的XML就不是DTO,而是“需要一个精心设计的中间层作数据组装”,那么,我们的观点在我来看,并没有什么不同。DTO就一定要是java的某个类?你的平面XML数据,也还是需要Transfer吧。我认为它也是DTO的其中一种表现形式。 俺的代码,就那么几个简单的转换方法,俺还真看不出重复在哪里呢。 并且用“尽量避免DTO”来作为开发的指导思想,意义并不大。这往往意味着概念不清楚。 下面该我发飚了,前面问的问题,一直没支持透传PO的人出来澄清。 “ 表示层直接耦合业务层,使得夹在它们之间的Service层,还有什么意义?表示层接受PO,是否意味着,表示层可以修改PO?PO可以在表示层修改,那么Domain层还有多少意义?我是否可以把PO在表示层完全修改好,然后传递回后台,后台只负责简单的持久化。业务对象(BO)还有必须存在的理由吗? ” |
|
返回顶楼 | |
发表时间:2006-03-18
nihongye 写道 我想先从交互过程来看看:
第一阶段:从外部获得数据,外部数据被组装成一定的格式. 第二阶段:使用第一阶段获得的数据,调用与该交互相关的业务处理方法 第三阶段:将响应数据交予表现层. 第一阶段数据应该被组装成怎样的格式?第二阶段,传递给业务处理方法的数据格式?第三阶段,响应数据的格式如何设计? 在第一阶段的数据格式容易,目标可只定为能解析出来. 在第二阶段,传递给业务处理方法的数据格式,由业务层的设计来决定,怎样的设计是为之好!? 在第三阶段,返回数据应该能体现出业务意义. 在这几个阶段,DTO或是PO的影子在那里? 考虑一个例子: 用户创建一个新的雇员 一,业务方法的设计 业务处理方法的责职假设为检查新雇员的相关属性是否合乎要求,改变雇员内部属性,持久化. 单从这个例子的责职来看,employee.create()或是employeeManager.create(employee)都是合理的. 二,外部输入格式的设计 是否在第一阶段,外部数据设计格式符合业务实体的模型(这样就可以利用BeanUtils之类工具来很快创建一个实体模型,如employee.post.department之类的输入格式)? 使用employee.post.department的输入形式耦合了业务实体的设计?这跟具体的解析方法相关,比如用BeanUtils这样的工具,那么耦合. 还是设计成emplyeeId,departmentId,不耦合,无论怎么变,都ok. 三,响应格式 大概同上,不废话. 说了那么多,DTO,PO这两个概念乱,想理清下自己的思维. 我前面举的例子,可是用来显示员工信息的阿。 DTO在更多情况下,是从后台传递到表示层,可以组合多个DomainObject的属性。来提供给用户必要的信息。 如果是对单个实体进行维护,我必须承认,DTO和PO确实长得很象。 我认为,得出传递DTO不必要的结论,多半是因为应用过于简单,或者对于构建RichDomainModel缺乏经验导致的。完全OO的DomainModel,确实是值得追求的。 |
|
返回顶楼 | |