锁定老帖子 主题:领域模型——想说爱你很难
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-04-30
就像martin在peaa里写的,有的时候,transaction script比domain model更适合,取决于问题的复杂度,也取决于开发人员对domain model的熟悉程度。domain model做得好当然能更好地解决问题,但用不好可能比transaction script更难维护。
我们目前所做的应用非常复杂。以前的系统就是transaction script的,但是已经没有办法维护。新的版本目前仍在开发中,ddd已经发挥出了它的威力。 我不认为分层架构会对ddd造成影响。因为ddd就是domain layer层的东西。 另外,在我们现在的代码中,大量的使用了strategy,state,proxy,decorator,composite等gof的模式,使我们的代码非常简化。如果使用transaction script,我怕又有许多switch 或者if要处理了。 to taowen: 你说的问题在rails里是很简单的,但在java里似乎还找不到什么办法。当然那样的代码更有诱惑力,更接近自然语言。但对我而言,觉得传统的方法也可以接受。 注入的问题是比较麻烦。我们现在都是在action层手工注入的。还好工作量不大。如果后边仍有问题,可能会采用registry来解决。虽然不那么优雅,但是保持一个良好的domain模型对我们而言目前更为重要。 如果只有crud或者很少有其它操作的系统,transaction script应该更适合一些。 |
|
返回顶楼 | |
发表时间:2007-04-30
to lihy70:
全部载入可是你说的,我可没说我要全部载入到内存中进行操作。Collection在你没有取得具体的成员之前,你怎么知道里面是有东西还是没东西?我完全可以把一个概念中的“集合”包含在Collection中,然后过滤其实是对集合求交集。 具体的过滤条件是由特定的Entities对象(也就是所谓的Collection)决定的。比如Employees对象就可以根据员工的职位,名字或者年龄过滤。深入每个过滤的实现,我们可以这么写: public class Employees : Entities<Employee> { public Employees Holds(Position position) { return new Employees(Find(Whose.Position).Is(position)); } public Employees OlderThan(int age) { return new Employees(Find(Whose.Age).IsBiggerThan(age)); } } 在基类中提供了Find方法和Query Object(Whose)。由于Find方法是重载的,所以你在Find(int)的时候是返回IntCriteria,Find(Entity<T>)的时候是EntityCriteria。 这样不就可以和Rail一样啦?FindByXXXX,而且同样保证静态类型,重构安全。 to 李超群: 不要放弃希望,Rails是一个好的榜样,破除了很多过去的迷信。阻碍框架变得优雅的,当然也有语言的因素。不过是不是决定性的,盖棺才能定论。至少我认为,在Java和.NET的平台上,设计思想也起到了很主导的作用。人们惧怕被指责破坏了分层,破坏了灵活性,诸如此类的。如果能够本着试试看还有没有简化的可能的思路去做的话,开辟一条新路出来也不是没有可能。 to Robin: 你的例子太棒了。Rails真神:) 这样的写法就是我想要的。 |
|
返回顶楼 | |
发表时间:2007-04-30
dlee 写道 lihy70 写道 这样不是很好吗?有什么不舒服的地方?
这样还是要配置在两个地方,UserEntity要配置在Hibernate中(通过mapping文件),UserOperation要配置在Spring IoC容器中。 客户端的代码以领域模型的写法为: User user = createUser(); //通过Spring代理实现的工厂方法,从Spring IoC容器中获取一个prototype对象。 //User是UserEntity和UserOperation共同实现的接口。 user.setId(userId); user.setXXX(XXX); user.setYYY(YYY) user.save(); 或者: User user = createUser(); user.setId(userId); user.get(); //要执行一下这个操作将持久对象从数据库加载进来 user.setXXX(XXX); user.setYYY(YYY) user.update(); 但是多出来了很多的类,有多少个PO类,就会多出来多少个对应的业务类。另外你还需要在Spring IoC容器中配置这些业务类,并且配置使用这些业务类的其他业务对象(就是这些业务类的“客户端代码”)中的工厂方法。 按照传统的方式的写法为: User user = userDao.get(userId); user.setXXX(XXX); user.setYYY(YYY) userDao.save(user); 或者: User user = userDao.get(userId); user.setXXX(XXX); user.setYYY(YYY) userDao.update(user); 不需要另外为每个PO类创建对应的业务类。 传统的写法确实是事务脚本,但是领域模型在这里并没有显示出足够的优势来,带来的麻烦要大于带来的好处。 是的,你说的没错。 但你完全可以把那些相同的操作抽象出去啊,例如get,update,save,delete之类的,这些完全可以通过代码生产工具自动生产。 所以就不用自己实现(关心)下面这些代码了: ....... user.update(); user.delete(); ....... 甚至: user.addGroup(Group group); user.remvoeGroup(Group group); 也可以自动生产。 你完全可以把自己的精力专注于自己的专有业务逻辑。而完全不用关心这些通用业务逻辑。 >但是多出来了很多的类,有多少个PO类,就会多出来多少个对应的业务类。 你不需要为每个PO都写一个业务类,只有那些有专有逻辑的PO才需要实现业务类。 从另一个角度说,即使你不用业务类,就在原PO里实现或其它的任何方案, 专有逻辑还是你总需要自己在某个地方实现的。 >另外你还需要在Spring IoC容器 >中配置这些业务类,并且配置使用这些业务类的其他业务对象(就是这些业务类的“客户端代码”)中的工 >厂方法。 至于这个嘛,我不懂Spring IoC.....,更不懂你为什么要这样说。 下面TaoWei同志大概说了这个想法(一开始我误解了它的意思,现在仍可能在误解中),你完全可以让自动生产的PO继承(或持有)一个什么东西,(注:所有的这些活动都可以是自动的)。而不需要什么TMD之类的IoC。 【注】 我认为用传统的用PO manager来管理PO也是一个不错的选择,它与DDD并不是对立的,可以根据具体情况选择两个方案。 |
|
返回顶楼 | |
发表时间:2007-04-30
rainlife 写道 DDD可以说是一种指导思想吧,在系统开发的时候,可以借鉴一些DDD的思想,但真正在做的时候,并不一定非要DDD。
就是这样的 |
|
返回顶楼 | |
发表时间:2007-04-30
taowen 写道 to lihy70:
全部载入可是你说的,我可没说我要全部载入到内存中进行操作。Collection在你没有取得具体的成员之前,你怎么知道里面是有东西还是没东西?我完全可以把一个概念中的“集合”包含在Collection中,然后过滤其实是对集合求交集。 具体的过滤条件是由特定的Entities对象(也就是所谓的Collection)决定的。比如Employees对象就可以根据员工的职位,名字或者年龄过滤。深入每个过滤的实现,我们可以这么写: public class Employees : Entities<Employee> { public Employees Holds(Position position) { return new Employees(Find(Whose.Position).Is(position)); } public Employees OlderThan(int age) { return new Employees(Find(Whose.Age).IsBiggerThan(age)); } } 在基类中提供了Find方法和Query Object(Whose)。由于Find方法是重载的,所以你在Find(int)的时候是返回IntCriteria,Find(Entity<T>)的时候是EntityCriteria。 这样不就可以和Rail一样啦?FindByXXXX,而且同样保证静态类型,重构安全。 嗯,你这样的办法已经是当下最佳了。我在想是不是可能让表达对象间关联的Collection能够更智能点,这样就可以直接在上面做查询了。查询的结果是另外一个Collection。如果能够这样的话,SQL表达的多表联合查询的逻辑就可以用操作Collection来表达。这样就可以写出一串话来: Divisions.Employees.FindWhoHoldsPosition(positionsTitledManager).FindWhoCalled("GuoXiao") 比用Query Object会更好看一些。 呵呵,可能是我不知道你不用“Collection”的方案是怎么做的,也没具体了解你用“Collection”的方案的意图,虽然你讲了: “这样不就可以和Rail一样啦?......”(可是我不懂Rail)。 如果(很担心)没理解错你的代码,那个你称之为“Collection”的Entities是一个持有(或者说可以触摸到)PO manager(例如Session)实例的对象,它的find或update将调用PO manager到数据库去获取或更新数据。所以你说:“我完全可以把一个概念中的“集合”包含在Collection中”。我原以为你说的Collection是象List的工具类。 如果是这样的话,那现在就理解了,Good idea! |
|
返回顶楼 | |
发表时间:2007-04-30
在错的地点,错误的时间,对错误的人发了一个错误的观点,所以请不要回复下面的话:
Rubby: 首先,我对Rubby之类的新生事物(也许你可以认为它old)充满敬意和欢迎。 对Rubby的简单灵活(这是我根据外面吹来的风感觉到的,我自己并了解Rubby)。 一个语言一开始可以很简单,它可以只专注于特定的事,拨开一些限制条件, 但随着它发展,人们就会期望用它来解决越来越多的问题,最后它就可能会由一块石头变成一座金字塔(如果它幸运的话),也可能Over了。 当它变成大众级别的语言时,如果它仍是一块石头,那只能说明那些设计C,C++和Java,以及那些在用它们的人都是一些白痴。 至于语法灵活,它的反义词是笨拙,人们一般把笨拙看成是贬义词,把灵活看成褒义词。 Java相对于C++来说以从语言设计上给它灵活性作了很多限制,但Java还是有这样那样的Code Rule。 所以我认为Rubby语法灵活并不是它的长处,而是短处,我相信如果Rubby(如果够幸运)将来有一天制定标准的话,将会为它的灵活烦透脑筋并会对它的灵活作出限制。 最后,当然语言是在(会)进化的,至于Rubby是不是下个幸运儿,那还要走着看。乐于看到Rubby的发展! |
|
返回顶楼 | |
发表时间:2007-04-30
lihy70 写道 也可以自动生产。
你完全可以把自己的精力专注于自己的专有业务逻辑。而完全不用关心这些通用业务逻辑。 你没有完全明白我的意思。我是说J2EE传统的分层的设计导致了在Spring+Hibernate的架构中按照领域模型的思路做开发,经常需要有一个与持久对象对应的业务对象(一个在持久层、一个在业务层,两者是一对一的关系)。在Spring中创建了这个业务对象之后,还需要去将这个业务对象与一个持久对象关联起来(当然你也可以将这些逻辑封装在某个方法中,例如在调用setId()的时候)。 lihy70 写道 你不需要为每个PO都写一个业务类,只有那些有专有逻辑的PO才需要实现业务类。
从另一个角度说,即使你不用业务类,就在原PO里实现或其它的任何方案, 专有逻辑还是你总需要自己在某个地方实现的。 我同意你的观点,只有那些有较为复杂逻辑(依赖于其他业务对象)的PO才需要实现一个对应的业务对象,但是这种情况是非常常见的。 lihy70 写道 至于这个嘛,我不懂Spring IoC.....,更不懂你为什么要这样说。
下面TaoWei同志大概说了这个想法(一开始我误解了它的意思,现在仍可能在误解中),你完全可以让自动生产的PO继承(或持有)一个什么东西,(注:所有的这些活动都可以是自动的)。而不需要什么TMD之类的IoC。 你既然不懂Spring IoC就没有什么需要跟你讨论的了,你可以去找一本书来学习一下为何做Java开发需要有一个IoC框架。 一些同志design by buzzword的倾向相当严重啊。 |
|
返回顶楼 | |
发表时间:2007-04-30
在Spring+Hibernate的架构中实现领域模型,发生的主要变化是:
不再需要有DAO类,DAO类所做的持久化逻辑被放在了领域模型对象之中(我上面叫做“与持久对象相对应的业务对象”其实不准确,准确的叫法是“领域模型对象”)。用我上面的例子就是将持久化逻辑放在UserOperation类中,不能放在UserEntity类中,绝对不能让持久对象自己来做持久化。这种改变好像类的数量一减一增,似乎应该于以前数量相同。其实不然,与持久对象对应的领域模型对象类的数量会比DAO类更多。这个领域模型对象因为同时拥有业务方法和持久方法,所以它是跨业务层和持久层的,这与J2EE的分层设计有些矛盾。领域模型对象与DAO对象有一个主要的差别是它们是有状态的,因此它们是prototype,而DAO对象是无状态的singleton。 一个主要的问题就是,在Spring+Hibernate的架构中,通常不能直接将一个PO当作一个领域模型对象来使用,而应该保持它的尽量简单,需要另外创建一个位于Spring IoC中的与之对应的领域模型对象。保持PO尽量简单还有一个好处就是可以重用它的类设计,例如与PO类相同的类设计也可以用来作为Web层的Command对象,并且可以将PO作为DTO来使用,传递到Web层。而对于领域模型对象来说,通常不要直接当作DTO对象来传递。 我为何总是将讨论范围局限在基于Spring+Hibernate的架构中呢?因为我知道小陶他们做Java开发也是基于这个架构。脱离开这个架构讨论什么是完美的设计,对于他们来说意义不是很大。 |
|
返回顶楼 | |
发表时间:2007-04-30
dlee 写道 lihy70 写道 也可以自动生产。
你完全可以把自己的精力专注于自己的专有业务逻辑。而完全不用关心这些通用业务逻辑。 你没有完全明白我的意思。我是说J2EE传统的分层的设计导致了在Spring+Hibernate的架构中按照领域模型的思路做开发,经常需要有一个与持久对象对应的业务对象(一个在持久层、一个在业务层,两者是一对一的关系)。在Spring中创建了这个业务对象之后,还需要去将这个业务对象与一个持久对象关联起来(当然你也可以将这些逻辑封装在某个方法中,例如在调用setId()的时候)。 >我是说J2EE传统的分层的设计导致了在Spring+Hibernate的架构中按照领域模型的思路做开发 ?不明白。我自己个人认为J2EE的分层设计是一个Great idea。至于是不是它导致了:“在Spring+Hibernate的架构中按照领域模型的思路做开发”, 我就不确定了。 >在Spring中创建了这个业务对象之后,还需要去将这个业务对象与一个持久对象关联起来(当然你也可以将这些 >逻辑封装在某个方法中,例如在调用setId()的时候)。 我想这个并不一定,用hibernate作DDD开发一定要用Spring吗?即使答案是Yes,那也可能是hibernate的问题,或你们把自己的想法固化了(只是假设),有没有换方案,换个想法,或从另一方去看问题...... 我的自己看法是J2EE的分层设计可能影响了,但并不是导致了Spring+hibernate.......。不可以把这个归责于J2EE的分层设计,更于Java语言无关。 dlee 写道 你既然不懂Spring IoC就没有什么需要跟你讨论的了,你可以去找一本书来学习一下为何做Java开发需要有一个IoC框架。 一些同志design by buzzword的倾向相当严重啊。 呵呵,谢谢,如果你明白我的意思,我是不会学Spring的 。 我是无神论者, Never say: Somebody(include God) said ...... |
|
返回顶楼 | |
发表时间:2007-04-30
我挺同意dlee和robbin的观点.增加了J2EE分层的模糊性,
并且领域模型对象感觉太超重了. |
|
返回顶楼 | |