|
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
|---|---|
| 作者 | 正文 |
|
最后更新时间:2007-10-09
首先声明:我是相当菜的,请轻拍
================================================================================== 从领域建模的角度出发,假设一个类Object中有一个聚合了的成员为Member,且Object和Member都是需要在数据库中持久化的。那么在写Object的类代码时,应该写成
public class Object{
private Member member;
}
还是
public class Object{
private Long memberId; //即member在数据库中的主键值
}
你可能会觉得我的问题很奇怪。的确,按面向对象的军规,当然应该是用 member。不过,我认为,如果整个架构都是基于Domain Model 模式,那可能没问题; 但如果是 贫血模型 + Transaction Script (如objectService作为script),情况就不一样了? 如果采用 贫血模型 + Transaction Script 模式,还要看有没有使用透明的自动持久化机制。 1.如果没有自动持久化机制,那么在已知object是持久化对象(即从数据库里找出来的)的情况下,object.getMember()是否代表一个有意义的实体? 得到的这个member到底是不是null,完全取决于object.getMember()之前有没有先object.setMember()。这就要求每个调用object.getMember()的地方,都要确认这一点. 如果从数据库里找出object后,做了很多很多事情,经过好几层的方法调用后,返回给一个jsp页面,这个页面敢不敢完全放心地用 <%=object.getMember().toString()%> 显示 member的值? 按照个人的经验,我总是非常忐忑的,因为它很有可能抛一个NullPointerException,让人下不了台。当然,我可以在显示它之前仔细研究返回它的各层代码,以确认有没有object.setMember()。但这样的话,第一层的代码(jsp)就会依赖于第三层、第四层......,如果要认为这不算强耦合,那只能以程序员非常细致的检查为代价了,稍有不慎,可能就要花很长时间来debug 所以我倾向于直接用"memberId"作为成员变量。object从数据库里取来时,memberId总是有意义的,它就是object表的外键值。任何一个想从object里拿member的调用者,老老实实按object.getMemgerId()的值去查一下数据库吧;当然,你会说这可能会增加数据库开销,但是我觉得,同时批量地从数据库里拿object和member的情形是不多的,如果真的有这种情形,在objectService中增加一个方法好了:getObjectAndItsMember()。总之,我的重点是,采用这种方法,调用者拿到object时,对member是不是Null,用不着关心,因为他已经死心了。----虽然这削弱了代码的部分功能,但维持了接口的确定性。 2.如果采用了自动持久化机制(如hibernate),那么采用object.getMember()方式一般情况下是没问题的。但是,有一个叫做“延迟加载”的东西会常常搞的你焦头烂额:虽然object.getMember().getMemberId()铁定不会报错,但是object.getMember().getSubMember().getSubMemberId()就难说了(假设subMember是member的成员)。如果你没有用延迟加载机制,那么上述语句不会有错,但是效率不行,这个大家都知道;所以大家一般都会用延迟加载机制,而使用这个机制又有限制。比如hibernate里就要求延迟查询时须与初始查询在同一个session内,如果要在web系统里这样玩,就要用一个非常反模式、怪异的openSessionInView(banq语);这样一来,单元测试就不好玩了,因为单元测试时没办法应用这个怪模式;更痛苦的是,如果你的web系统里启动了一些后台线程,由于这些线程不是WEB用户,自然没办法应用openSessionInView模式,因此延迟加载也就用不了,object.getMember()也就不能用了,那你只能在在objectService里增加一个通过ID查找Member的方法来找到object的member,这时候你的代码就很丑了,既可以通过object.getMember()拿到member,又可以通过objectService.getMemberById(object.getMemberId())拿到,同一种功能,在不同的环境下,要用不同的机制实现!--不要以为这是什么大不了的事情。 由于上面所说的情况,我对hibernate产生了强烈质疑,甚至对自动持久化机制本身都很怀疑了。数据库的存取,真的应该做成透明的、自动的吗?一方面,正如上文所述,很难在不产生二义接口的情形下,彻底的保证透明和自动化;另一方面,扯得有点远了,我觉得持久化本来就不应该做成自动的,因为要不要持久化,在什么时机持久化,应该由程序员根据业务主观地确定,托付给一个自动的机制,不是自缚手脚吗? 我不但怀疑ORM的自动化机制,也怀疑对象关系的O/R映射。实际上,我觉得,如果要映射对象之间的关系,也就是要使object.getMember()有意义,也就一定要用自动化的ORM,也就会产生上面所说的问题。所以,这也是我选择memberId作为成员变量的原因。 进一步说,我认为,凡是要记录对象状态的系统中,都不能比较自然的使用O/R框架,都不可能实现理想主义的OO模式:忘却数据库的存在,然后在Domain Object里应用各种OO技术。 可是,这又怎么样? 我们仍然可以在Domain Object体系结构里应用多态,在trancaction object里使用Strategy、State等模式,同样实现了较好的弱耦合和强内聚,同样提高了软件的生产率,这难道会很糟糕吗? 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
最后更新时间:2007-10-10
跟楼主有同感,希望有人进来指点迷津
|
|
| 返回顶楼 | |
|
最后更新时间:2007-10-10
OK,OK 我承认你说的很有道理
但是你记得当年的DTO这么个怪东西么?大家不满意,认为增加了无聊的工作量,要把VO/PO用一个东西框起来,要POJO一窜到底,要透明的持久化,于是就又出来了怪怪的open session in view. 现在又觉得open session in view是不好的,是反模式的,于是大家有开始考虑是不是不要那么自动,要自己能够控制. 是不是我们又开始走回头路了?再这样讨论下去,我看DTO又要还魂了. 在现有的技术框架里,view和persist永远有矛盾.除非有办法把view和persist放在同一个context里,换句话说,改良一下open session in view.想办法把persist context inject到domain object里,问题是现有的容器有这种功能么?? 真的出现这种功能,我们又会不会觉得太霸道了? 至于我所参加的项目,首先按照最简单的做法,并且使用open session in view(反模式就反模式,你给个不反而且好用的我马上换),如果还有lazy问题,我们在service里就再添加一个方法 fillAllChildren. |
|
| 返回顶楼 | |
|
最后更新时间:2007-10-10
你说的有道理,原来用了DTO还可以略去 open session in view 的麻烦,我今天才明白
问题是,我们为了适应 自动的、映射了对象关系的ORM,做了这么多疲累的额外工作,进行了这么多怪异的设计,最终,我们还是成功了;但是 “自动的、映射了对象关系的ORM”,到底有哪些好处? 我不是在抬扛,也相信具体的好处也是有的,我们可不可以现在就总结一下这些好处,然后再总结一下实现这些好处的代价? |
|
| 返回顶楼 | |
|
最后更新时间:2007-12-22
质疑得好,早就该质疑了
hibernate总想解决所有问题,让事情完美,结果越搞越乱 |
|
| 返回顶楼 | |
|
最后更新时间:2007-10-18
我从领域模型的角度考虑这个问题:
用Member还是memberId的区别在于:用Member则Object到Member是有关联导航的(也就是从Object可以直接找到Member),而用memberId就不是关联了(虽然语义上也是关联的),要找到Member必须通过一种重建机制(如数据库查询)。 如果Member概念只属于Object,建议还是用Member,持久化Object时自动持久化Member,所有对Member的访问都应该通过Object来进行。 反之,用memberId。 另外,Object与Member如果都是贫血的,还是用memberId吧。 |
|
| 返回顶楼 | |
|
最后更新时间:2007-10-18
一句话,在实际情况中,怎么顺手怎么来
|
|
| 返回顶楼 | |
|
最后更新时间:2007-10-31
我觉得这是一个大多数使用 ORM 的开发者的共鸣。
我也有这么多的困惑,所以我选择了 iBATIS |
|
| 返回顶楼 | |
|
最后更新时间:2007-10-31
深有同感
|
|
| 返回顶楼 | |
|
最后更新时间:2007-12-22
zengcuoan 写道 但是,有一个叫做“延迟加载”的东西会常常搞的你焦头烂额:虽然object.getMember().getMemberId()铁定不会报错,但是object.getMember().getSubMember().getSubMemberId()就难说了(假设subMember是member的成员)。如果你没有用延迟加载机制,那么上述语句不会有错,但是效率不行,这个大家都知道;所以大家一般都会用延迟加载机制,而使用这个机制又有限制。比如hibernate里就要求延迟查询时须与初始查询在同一个session内,如果要在web系统里这样玩,就要用一个非常反模式、怪异的openSessionInView(banq语);这样一来,单元测试就不好玩了,因为单元测试时没办法应用这个怪模式;更痛苦的是,如果你的web系统里启动了一些后台线程,由于这些线程不是WEB用户,自然没办法应用openSessionInView模式,因此延迟加载也就用不了,object.getMember()也就不能用了,那你只能在在objectService里增加一个通过ID查找Member的方法来找到object的member,这时候你的代码就很丑了, 你这些担心都是不存在的 1、object.getMember().getSubMember().getSubMemberId()不会出错,lazy load可以支持无数层对象。 2、openSessionInView不是反模式,banq说了不算。 3、openSessionInView单元测试没有问题,你可以照葫芦画瓢搞个openSessionInTest么。 4、“由于这些线程不是WEB用户,自然没办法应用openSessionInView模式,因此延迟加载也就用不了” 这个担心也是想当然,因为在事务性方法内部根本不需要openSessionInView,因为这时候session还是开着的呢。 |
|
| 返回顶楼 | |







