论坛首页 Java企业应用论坛

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

浏览 25355 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-14  
jackyz 写道
这个 Implement 会调用远程 Session 服务的包装接口,获取所需的对象(这个过程对于外部代码会是透明的)。


这句话已经很明确无误的否定了你自己的设想了。Hibernate是一个底层框架,不是一个常驻内存的服务,或者用我们流行的用语来说,叫做容器。如果Hibernate企图去做这种分布式的应用,它就不是一个ORM,而是演变成了一个支持远程访问的容器了。

而支持远程访问的容器这并不是Hibernate要做的事情,是SpringFramework,是EJB3要做的事情。打个比方来说,你是销售经理,我是软件架构师,你负责和客户打交道,打单,而我负责领导软件开发团队开发,设计软件架构。 这个时候你突然说软件开发和客户之间的距离太远了,我们必须让架构师具备销售经理的素质和能力,让架构师去整天跑客户,打单,陪客户吃饭,这不是本末倒置吗?

作为一个ORM来说,即使不考虑到远程调用,其实也有很多的功能需要在ORM之外来解决,例如数据库资源的透明化管理,例如抽象的声明式事务管理,例如合理的异常层次设计和异常处理方式,例如合理的Dao接口隔离等等等等。这些工作都是必须在ORM之外来完成的。ORM是一个相当底层的框架,它需要运行在一个受控的管理环境中(managed Environment),而上面提到的这些功能都是由这个受控的管理环境来实现的,而且包括你提到远程调用问题,实际上也是未来受控环境应该提供的功能之一。

目前来说,SpringFramework实际上就是这个受控的管理环境,他处理了ORM上层的数据库资源透明的管理,容器可声明的事务管理,异常的处理流程和事务回滚等等。当然,Spring目前对远程调用的支持还很不成熟,实际上对远程调用的支持也是我最期待Spring改进的方面。

就未来展望,EJB3实际上就是你期望的东西,EJB容器提供了这个受控的管理环境,当然继承自EJB2的远程调用也会得到很好的支持,而基于Hibernate的EJB3持久化标准也提供了类似Hibernate的ORM功能,不过会在远程调用上提供了透明的支持。

所以基本上可以这样来对比:
Spring+Hibernate  vs  EJB3
Spring和Hibernate合在一起才有能和EJB3相提并论的比较基础,即都提供了一个完整的企业应用基础设施。他们的区别则是面向不同的市场。
1 请登录后投票
   发表时间:2005-03-14  
jackyz 写道

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

对于你说的小型项目开发人员同时负责前后台的情况,你认为对于只需要传递三个属性的情况下,却传递了几十个属性合理吗?
更极端的情况是“获取客户列表”,这时仅仅需要包含客户代码和客户姓名两个属性的VO集合。直接传递PO将会传递客户的集合过来。

另外,关于DomainObject同DTO“高度类似”的问题。
不知道我说的对不对,你之所以感觉重复是因为你的DomainObject中
没有包含本来该封装在它里面的业务逻辑。
来做一个类比吧:
DomainObject中的Class同影射到数据库中的表也是“高度类似”吧。
某种意义上说DomainObject到数据库表的映射同到DTO的映射都是
对象在不同环境的不同表示而已。
从关系数据库的观点来看,他把对象看作表中的行。到了业务层它变成了
一个个的DomainObject。到了应用层它又变成了面向用户的信息。针对
不同的环境需要有不同的表示,这也意味着要经过不同的变换。

就拿数据库来说吧。数据库里面的表大多数情况下是不能直接给用户查看的。
要不报表这种东西就完全没有必要了。数据库的数据需要收集、抽取、转换、
计算等处理后才能得到报表需要的信息,呈现在用户面前。

如果让前台开发人员直接使用PO,那无异于让用户直接查看数据库中的表。
是的,如果你开发的系统自己就是用户,那么查询的功能确实完全可以不要,
自己到数据库的表里找就是了,呵呵。不过即使是这种情况,你也会借助SQL
来转化数据。而不会一个表一个表的查找。
扯得远了点,明白我的意思了吗?
0 请登录后投票
   发表时间:2005-03-14  
引用
如果让前台开发人员直接使用PO,那无异于让用户直接查看数据库中的表


这说明你还不懂ORM。

关于DTO的话题,论坛已经讨论的过多了,只怕有上千帖了吧,我看没有必要再讨论一遍了。要讨论DTO,先去翻翻以前的讨论,如果没有新的观点,还是打住吧。
0 请登录后投票
   发表时间:2005-03-14  
robbin 写道
引用
如果让前台开发人员直接使用PO,那无异于让用户直接查看数据库中的表


这说明你还不懂ORM。

关于DTO的话题,论坛已经讨论的过多了,只怕有上千帖了吧,我看没有必要再讨论一遍了。要讨论DTO,先去翻翻以前的讨论,如果没有新的观点,还是打住吧。

呵呵,没看见吗?前面说得很清楚,这是一个类比。

没错,是讨论了很多。人家都说了是在炒冷饭没看见?
同时我并没有看到支持直接传递PO的有力证据,您老有给出过吗?同样反对DTO的理由也不充分,最多的就是嫌麻烦。

你如果不感兴趣,或者认为自己的观点已经没有什么需要阐述的了,那就让我这个不懂ORM的人同人家炒炒冷饭好吗? 您老就高抬贵手吧。
0 请登录后投票
   发表时间:2005-03-15  
Shit,写了一大段,竟然丢了要重写,苦死我了。:?

robbin 写道
Hibernate是一个底层框架,不是一个常驻内存的服务,或者用我们流行的用语来说,叫做容器。如果Hibernate企图去做这种分布式的应用,它就不是一个ORM,而是演变成了一个支持远程访问的容器了。


嗯,我知道这不是ORM的职责,也没有期望Hibernate能做这件事。

实际上,用Spring也好, EJB3也罢,或者上面提到的“Hack Hibernate CGLIB Process”,甚至是手写代码加上Factory,Servlet,Serializable也能实现一个这样的“透明RPC框架”,如何实现其实并不是问题的关键。

这个冷饭贴,实际上是我的突发奇想。论证一个突发奇想也许并不是为了证明它或者实现它,即使最后的结论是“这真是一个无聊的想法”,但在它的论证过程中,我们也许能更清楚“Why It's EVIL”。我的Point在于模式的选择,在Remote场景,在我们期待的RIA时代,除了DTO模式,除了被裁减的PO,难道我们就确实没有其他的选择了?



是的,partech,我知道你说得很有道理,DTO允许前后台并行开发(这似乎是一个致命优点);我也知道我的DomainObject设计得很烂,它们不Rich,缺少太多的业务逻辑;而且,因为没有其他成熟方案,我目前的项目也必须采用DTO。但,“展开想象的翅膀”,权当做个白日梦,总是可以的吧。

partech 写道
反对DTO的理由也不充分,最多的就是嫌麻烦。


嫌麻烦难道不是一个很好的理由么?很多人就是因为嫌麻烦而不愿意使用DTO(包括我 )。DTO被大家众口一词的诟病,难道他们都是人云亦云?EJB确实是失败了(WithOut EJB Patterns),但这是否能够说明它的设计思路就是一无是处呢?

一个不可否认的事实是——DTO的引入,实际上造成系统中出现了两个(或者说N个)对象体系——Server端面对的是Rich的DomainObject,Client端面对的是Flat的DTO。我实在是搞不明白,一个高度面向对象的系统中,为什么要有多个对象体系呢?仅仅是为了传输?

就拿这个例子来说:
partech 写道
数据库里面的表大多数情况下是不能直接给用户查看的。
要不报表这种东西就完全没有必要了。数据库的数据需要收集、抽取、转换、计算等处理后才能得到报表需要的信息,呈现在用户面前。


数据库的数据需要收集、抽取、转换、计算可以交给DAO,那么,呈现又为什么不能交给RemoteClient呢?

单就呈现这部分而言,如果引入DTO,服务端要做的实际上就是“从DomainObject中取出需要的属性,塞入DTO中”,然后传输,RemoteClient端做的就是“从DTO中取出需要显示的值,放到相应的Widgets中”,多么令人乏味的工作?

如果传来的直接就是DomainObject(当然,这里没有考虑传输代价),由RemoteClient来负责“从DomainObject中取出需要的属性,放到相应的Widgets中”,这又有何不可?至于具体的显示,直接调用方法或者ONGL,那不会是个大问题。更重要的是,无论在系统的前端还是后端,我所理解的是一个统一的对象体系。

去掉了DTO,也就可以不管它的生产、转换、组装、传输、消费……。对于有着“懒惰这个优良品质”的程序员来说,能省的,就别留着,不是么?

ok,问题时间。

问题一,变化。让DomainObject跨越系统的所有层次,对它的修改必然困难重重,但我们也不在石器时代,Refactor工具至少能让人的体力劳动能轻松点。况且,修改接口也并非闻所未闻的事嘛。

问题二,并行。没有DTO的隔离,前后端的开发难于并行进行,是的,的确难于,但也许并非是不可能的吧,Mock是否有助于此呢?堂堂DomainObject至少也得有个Interface才象话吧。

问题三,浪费。确实,我强买强卖地带了很多“用不上的属性”(至少目前用不上),虽说,我们可以LazyLoad,我们有LocalCaching,但的确浪费了一些宝贵的带宽,罪过罪过。但,比起“设计和维护N个DomainObject在M种情况下的各个DTO”,也许我可以选择——认了。

问题四,做梦。不要忘了,所有美丽的幻象都是建立在“不考虑传输代价”的空中楼阁之上——谁说不是呢?不过,也许某天有位GavinKing之类的大仙“一不小心踩到香蕉皮”帮俺们解决这个问题了呢(LazyLoad可以得到Fullfeature的PO和合理的传输粒度,Caching可以减少RPC的次数)?且让我再梦一会儿吧。

……

共产主义还未实现,进度总是在生气,DTO仍然要继续。

梦总是要醒的,生活是残酷地~~
0 请登录后投票
   发表时间:2005-03-15  
jackyz 写道

嫌麻烦难道不是一个很好的理由么?很多人就是因为嫌麻烦而不愿意使用DTO(包括我 )。DTO被大家众口一词的诟病,难道他们都是人云亦云?EJB确实是失败了(WithOut EJB Patterns),但这是否能够说明它的设计思路就是一无是处呢?

一个不可否认的事实是——DTO的引入,实际上造成系统中出现了两个(或者说N个)对象体系——Server端面对的是Rich的DomainObject,Client端面对的是Flat的DTO。我实在是搞不明白,一个高度面向对象的系统中,为什么要有多个对象体系呢?仅仅是为了传输?

单就呈现这部分而言,如果引入DTO,服务端要做的实际上就是“从DomainObject中取出需要的属性,塞入DTO中”,然后传输,RemoteClient端做的就是“从DTO中取出需要显示的值,放到相应的Widgets中”,多么令人乏味的工作?

如果传来的直接就是DomainObject(当然,这里没有考虑传输代价),由RemoteClient来负责“从DomainObject中取出需要的属性,放到相应的Widgets中”,这又有何不可?至于具体的显示,直接调用方法或者ONGL,那不会是个大问题。更重要的是,无论在系统的前端还是后端,我所理解的是一个统一的对象体系。

去掉了DTO,也就可以不管它的生产、转换、组装、传输、消费……。对于有着“懒惰这个优良品质”的程序员来说,能省的,就别留着,不是么?

呵呵,我相信假如你看到这种转化是必须的,那么不管它是否麻烦你都会乐此不疲的使用,就像使用Hibernate一样。
为了说清楚我的观点,你需要同我一起上升到1千米的空中来审视所开发的系统。
从前向后看是三个领域,分别是应用、服务和数据库。在服务和数据库之间使用ORM大家都很熟悉
可以为什么不能把DomainObject直接持久化到数据库呢?问题就在于两者看待信息的观点不同。
服务层包裹中的业务层是数据和行为封装在一起的业务对象。关系型数据库中的信息是表中的行。
同样的应用层负责同用户交互,它的观点就是用例的观点,能够很好匹配的模式就是输入是什么,系统状态如何变化,输出是什么。
应用层的方方面面无不是围绕他们展开的,输入的是Action,系统状态变化被抽象为Model,输出的是View。
在用例中你不会看到有关业务对象如何实现的描述,这要到设计业务层才会考虑。但是仅仅需要这些信息就完全能够
设计出应用层需要服务层完成的接口了。这时候业务层还未设计出来,所以设计成传递PO的服务层接口是完全不可能的。
相反设计出传递VO符合用例的服务接口则是唾手可得的事情。我们反过来看看传递PO的不合理。服务接口是基于用例的,
对于显示客户信息的段落我们可以这样描述:
用户输入客户代码,系统确认客户代码有效,系统显示客户的基本信息,包括客户代码、客户姓名、客户证件类型、客户证件号码和客户的状态。
对应的接口方法为 CustomerBasicInfo OnCustomerCodeInput(String customerCode);
public class CustomerBasicInfo
{
public String Code;
public String Name;
public String CertificateType;
public String CertificateNO;
public String Status;
}
如果采用传递PO则变为 Customer OnCustomerCodeInput(String customerCode);
问题在于Customer对象现在根本无法定义。而且因为业务层还未开发出来,不可避免的开发业务层的过程中会造成接口的不稳定,同时
如果需要重构业务层的话,应用层也不得不被迫重构。
《敏捷软件开发中》里有这样一个开发原则,具体的依赖抽象的,朝稳定的方向依赖。直接传递PO是一个反例。

Hibernate是一种ORM,本质上就是一种转换,把数据库的行转化为DomainObject,或者相反。DTO的Assembler做着同样的事情。
为什么没有人对使用ORM提出质疑,却频频觉得编写Assembler麻烦呢?

jackyz 写道

数据库的数据需要收集、抽取、转换、计算可以交给DAO,那么,呈现又为什么不能交给RemoteClient呢?

呈现是Client的事情,但是组装DTO是Assembler的事情。


jackyz 写道

ok,问题时间。

问题一,变化。让DomainObject跨越系统的所有层次,对它的修改必然困难重重,但我们也不在石器时代,Refactor工具至少能让人的体力劳动能轻松点。况且,修改接口也并非闻所未闻的事嘛。

请参考Robort Martin的大作《敏捷软件开发中》,频繁修改接口说明依赖关系有问题。

jackyz 写道

问题二,并行。没有DTO的隔离,前后端的开发难于并行进行,是的,的确难于,但也许并非是不可能的吧,Mock是否有助于此呢?堂堂DomainObject至少也得有个Interface才象话吧。

在没有经过艰苦的设计出业务层之前,你的接口中的东东同样是不稳定的。而且你的PO如果简单到同VO差不多,我实在看不出在他上面再来一个接口的必要。

jackyz 写道

问题三,浪费。确实,我强买强卖地带了很多“用不上的属性”(至少目前用不上),虽说,我们可以LazyLoad,我们有LocalCaching,但的确浪费了一些宝贵的带宽,罪过罪过。但,比起“设计和维护N个DomainObject在M种情况下的各个DTO”,也许我可以选择——认了。

浪费带宽其实还是小事。目的不明确才是最根本的。

jackyz 写道

问题四,做梦。不要忘了,所有美丽的幻象都是建立在“不考虑传输代价”的空中楼阁之上——谁说不是呢?不过,也许某天有位GavinKing之类的大仙“一不小心踩到香蕉皮”帮俺们解决这个问题了呢(LazyLoad可以得到Fullfeature的PO和合理的传输粒度,Caching可以减少RPC的次数)?且让我再梦一会儿吧。

呵呵,有没有梦见 XML mm在向你招手微笑?! 
0 请登录后投票
   发表时间:2005-03-15  
youcai 写道

在我们的设计中,如果组装属性属于业务,会放在DomainObject中,如下
...
public int getStatus(){
    return status.getInt();
}
...

如果为了显示,需要组装,有前台自己完成。

对于多余属性,个人不认为会对前台造成什么影响,泄密?使用没有代码完成功能的编辑器?

在我们的设计中,不考虑分布式,基本就不考虑DTO。

嗯,如果你所写的方法是在Customer中,那么恭喜你,你会后很多机会来重构Customer,因为客户签订的协议会与日俱增。
Customer是很底层的业务对象,不要老是去变动它!

多余的属性,会使得你的接口方法变得圆滑,含糊不清。
0 请登录后投票
   发表时间:2005-03-15  
看来我们需要一个DVM了,Domain/View Mapping,根据定义自动将Domain Ojbect转换为View Object。
0 请登录后投票
   发表时间:2005-03-15  
flyisland 写道
看来我们需要一个DVM了,Domain/View Mapping,根据定义自动将Domain Ojbect转换为View Object。


这个想法很好,但是可行性不高,主要是因为Domain Object和View Object的信息量相差太远。如果Domain Object包含的信息量是1的话,那么View Object包含的信息量可能是10,Domain Object只要包含完成任务的信息就足够了,但是View Object是直接面对最终用户的,所以View Object除了需要必需的Domain信息之外,还需要大量的与人机交互相关的信息。诸如层次信息、分组信息、排版信息、交互控制逻辑、事件处理逻辑、数据校验逻辑、帮助信息及提示信息等要素是必不可少的,这些交互控制辅助信息与Domain Object无关,但是却非常重要,客户是否愿意接受系统甚至还取决于这些要素,现在的口号是“科技以人为本”,View Object的复杂程度和重要程度一点都不会逊色于Domain Object。
0 请登录后投票
   发表时间:2005-03-16  
用webwork之类的框架中开发web应用,我们通常的做法就是不用DTO,直接使用PO,View层代码通过ONGL/JSTL来构造界面.大家普遍认可这种做法----因为它的简单方便.而且认为在这种情况下使用DTO是不必要的反模式的.这是目前大家都基本认可的事实.

换一个角度,我们也可以认为,webwork的action程序段就是一个简单的Application,它也处理与用户的交互(一次一个Page),它也与我们的Service层进行交互.也就是说,对Service层而言,它实际上就是一种所谓的"前端代码",所不同的是,这个"前端代码"运行在与Server相同的一个JVM之中.

既然WebWork的"前端代码"直接使用DomainObject是合理的,那么为什么其他的"前端代码"直接使用DomainObject就不合理了呢?

是的,环境不同.不同的JVM,网络传输的代价.除此之外,Service的其他Client无论从哪里来看,与WebWork的Client都没有什么本质的不同.

DTO除了Transfer的意义以外,是否并非一种本质上的必须?
DTO存在的本质理由是否仅仅就是这种环境的差别?

如果这是DTO存在的本质原因,而且如果引入一种机制能够消除这种差别(或者尽量减少这种差别带来的影响),那么DTO的存在理由是否就不存在了呢?

partech 写道

...
这种转化是必须的,那么不管它是否麻烦你都会乐此不疲的
...
同样的应用层负责同用户交互,它的观点就是用例的观点,能够很好匹配的模式就是输入是什么,系统状态如何变化,输出是什么。
应用层的方方面面无不是围绕他们展开的,输入的是Action,系统状态变化被抽象为Model,输出的是View。
....


UI的Client与用户交互当然需要MVC,但是,我们是否需要将UI对于Model的需求与DTO的职责混合起来呢?

Webwork的开发中Service提供给Action的是DomainObject,我们通常通过Action本身来做转换,得到用于显示的Model.那么,在RichClient的开发中,(如果我们能在代价不高的前提下得到DomainObject)为什么我们就不能这么做呢?

// 比如 Client 中的 CustomerInfoWindow
// 显示的时候 CustomerInfoWindow 从 CustomerInfoController 中获取相应的值,显示
// CustomerInfoModel (同时也是Controller); 可以是这样的
public class CustomerInfoController {
    // 用于显示的属性
    private String code;
    private String name;
    private String certificateType;
    private String certificateNO;
    private String status; 
    // 构造器
    public UserInfo(String code); {
        this.code = code;
    }
    // 初始化事件
    public void onInit(); {
        // 远程获得Service提供的 CustomerInfo 实例 (DomainObject);
        CustomerInfo c = service.getCustomerInfo(code);;
        if (c != null); {
            // 设置用于显示的值
            name = c.getName();;
            certificateType = c.getCertificate();.getType();;
            certificateNO = c.getCertificate();.getNO();;
            status = c.getStatus();.getName();;
        }
    }
    // get and set methods
}
0 请登录后投票
论坛首页 Java企业应用版

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