论坛首页 Java企业应用论坛

谈一谈贫血的Domain Logic问题。

浏览 42600 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-21  
robbin 写道
我的理解是Martin大叔所谓的domain object是“领域模型”,它是一个针对业务逻辑抽象的模型,和软件编程根本毫无关系。即使你不开发这个软件,你仍然需要抽象你的业务逻辑得到你的领域模型。这个领域指的是你所从事的行业,而不是狭隘的持久化类

并且领域模型的建模也是在需求分析阶段,或者在需求分析阶段之前完成的事情。具体到编程的过程中,领域模型并非对应某一个Java类,如果一定要强行对应的话,(业务对象,Dao接口,Hibernate实体类)他们合起来统称领域模型在Java语言的实现。把 商业建模范畴的“领域模型”拿来当做Hibernate编程中的实体类,根本就是牛头不对马嘴!

不可否认,实体模型是实现商业逻辑的基础结构.

从这个意义来说,即使可以说实体模型不代表领域模型,我们仍然可以认为它是领域模型的核心部分. 分辨这些区别我个人觉得没什么意义.

如果非要讨论下去,不如可以这样说:

实体模型不能代表全部的商业领域模型,实现商业领域模型仍然需要一个商业逻辑层来实现.

但是,这个无可非议的正确观点是否就意味着实体模型只能代表数据,而不能拥有属于他自身的商业逻辑呢? 这点我人为可以值得深思!
0 请登录后投票
   发表时间:2005-03-21  
firebody 写道
robbin 写道
我的理解是Martin大叔所谓的domain object是“领域模型”,它是一个针对业务逻辑抽象的模型,和软件编程根本毫无关系。即使你不开发这个软件,你仍然需要抽象你的业务逻辑得到你的领域模型。这个领域指的是你所从事的行业,而不是狭隘的持久化类

并且领域模型的建模也是在需求分析阶段,或者在需求分析阶段之前完成的事情。具体到编程的过程中,领域模型并非对应某一个Java类,如果一定要强行对应的话,(业务对象,Dao接口,Hibernate实体类)他们合起来统称领域模型在Java语言的实现。把 商业建模范畴的“领域模型”拿来当做Hibernate编程中的实体类,根本就是牛头不对马嘴!

不可否认,实体模型是实现商业逻辑的基础结构.

从这个意义来说,即使可以说实体模型不代表领域模型,我们仍然可以认为它是领域模型的核心部分. 分辨这些区别我个人觉得没什么意义.

如果非要讨论下去,不如可以这样说:

实体模型不能代表全部的商业领域模型,实现商业领域模型仍然需要一个商业逻辑层来实现.

但是,这个无可非议的正确观点是否就意味着实体模型只能代表数据,而不能拥有行为呢? 这点我人为可以值得深思!


谁说实体模型不能有行为了??
0 请登录后投票
   发表时间:2005-03-21  
Martin所说的“贫血的领域模式”是强调不要忽略了面向对象最初也是最基本的东西----“封装”。不能因为封装得不好,导致没有简化问题反而增大了复杂性,就怀疑这种思路的正确性。
0 请登录后投票
   发表时间:2005-03-21  
好的,纠正过来! 
0 请登录后投票
   发表时间:2005-03-21  
firebody 写道
robbin 写道
不说了,OCP有什么原则来着?什么叫做单一职责?好像还有一个叫做“分离关注点”的时髦词汇怎么讲来着?

引用

让 PO具备行为并把大部分商业逻辑按照职责分离的原则分离PO模型中,这样的设计无疑大大提高架构内聚度,更符合OO设计的原则。


把所有的职责都塞到一个类里面实现就能够提高架构的内聚度吗?就更加符合OO设计的原则吗?


或许你理解 错我的本意!
把所有的职责都塞到一个类里面实现就能够提高架构的内聚度吗

应该改为:
把本属于POJO的职责都塞到POJO模型里面实现就能够提高架构的内聚度吗

这样提高的内聚度应该是显而易见的.
这里的内聚度更确切来讲,应该是完成商业逻辑的相关类之间的聚集度. 现在由原来的Service Layer + POJO ------> POJO( 含调用Service Layer-->这里的Service Laye 仅仅实现简单的访问底层数据的逻辑) ,
从这个角度来讲,我还是认为内聚度大大提高.

从外层应用来讲:
对于一个复杂的业务逻辑,他可以这样做:

BussinessA  Strategy-->POJOA, POJOB

如果是原来的确Service Layer,因为涉及到需要Service保存POJO状态问题在于.于是,很多情况下,不得不这样设计:


public class ServiceB{

   public void doBusinessA();{
                 serviceA();.business1();;
                 serviceC();.business2();;
                 .......
   }
}


在简单的应用中, ServiceB 可能是一个Action.

如果是具备行为的 POJO
很可能这样即可:

businessA.{
   POJOA.doBusiness();;
}


从上面代码可以看出:
让POJO具备一定行为,以致POJO模型内聚度大大提高.,降低软件构假复杂度,我认为这是我发起这讨论的最根本原因.


你的这个例子这么简单能证明什么呢?

其实你的主张就是把持久化实体类的持久化操作附着到实体类上面去,而不是分开。举例来说,也就是说Account类的增删改查不应该单独分离到AccountDao接口去,而应该把增删改查操作放在Account类里面来完成,对不?

那么我在解释这个问题之前,必须澄清一点,就是这个问题的讨论,即持久化操作是否应该单独抽象一个DAO层的问题是和Martin Fowler提到了贫血的领域模型毫无关系的。实际上传统的Entity Bean就是这种把持久化操作附着到Entity Bean本身去的,但是Martin Fowler一样在说,这种Entity Bean就是贫血的。

好了,我们现在把其他无关因素排除了,focus到这个话题上,就是我们是否需要DAO接口的问题了。因为按照你的观点,如果把对象的增删改查都放在实体类上面,其实我们就不需要DAO接口层了,业务对象和Web Action都可以直接调用实体类本身来完成持久化操作了。

大概在2003年以前,我一直就是采用这种模型的,但是从2003年开始,我就改成了分离一个DAO层来专门处理持久化操作了。原因是多方面的,从技术角度来考量的话,分离就有很多好处,Rod Johnson在《without EJB》第10章持久化里面就详细阐述了需要DAO层的理由,我建议你看一下,这里不复述了。只谈一下除了Rod Johson提到理由之外的理由:

作为保持状态的实体类,他的职责是保持状态,而不负责状态的持久化。如果把持久化操作也放在实体类中,一方面来说,把两种不同的职责放在一个类中,并不符合OCP的单一职责的原则,然而更重要的原因是会带来实体类的不稳定的问题。

在我的理念中,实体类以及实体类关联关系是一个软件系统中最稳定不变的部分,在整个软件系统中,各个层面都需要涉及到实体类的操作,如何划分实体类,以及确定实体类关联是最费考量的,确定了这一点,整个软件系统就底层依赖关系就被确定下来了(实体类的属性可以变化,由于软件系统对实体的操作都是以实体类为单位的,所以实体属性的变化不会造成系统不稳定),上面的DAO层,业务层,Web层都只对实体类产生单向依赖。

如果你把DAO层合并到实体类中,请注意本来Web层是不依赖于DAO层的,Web层只依赖实体类(因为它要展现实体类状态),但是由于你合并了,使得Web层也变得依赖那些DAO层的操作了。这样的结果就是让软件系统的耦合性提高,可伸缩性降低,可维护性降低。

DAO层的变动是大于实体类变动的,实体类基本上稳定不变,而软件系统的需求变更几乎一定会带来DAO层的变动,但是并不会带来实体类层的变动(会带来实体属性的变动,但是很少会影响到实体类本身)。所以DAO层的变动频率远远大于实体类。那么当你把DAO层操作合并到实体类以后,其结果就是让实体类的变动频率等于DAO层的变动频率。那么就会造成本来稳定的实体类层变得变得频率高了很多,而实体类的变动会波及到软件系统的每个层面,造成软件大面积的相关性和不稳定。

请记住很重要的一点:实体类是有状态的类,DAO类是无状态的类,在整个软件系统中,只有两种类有状态,一个是实体类,一个是HTTP Session。有状态的类会带来很高的代码相关性,应该尽量减少有状态类的影响范围,尽量减少有状态类的变动频率,应该尽量减少有状态类的职责。

而你把DAO操作合并到实体类以后,结果就是扩大了有状态类的代码相关性的范围和影响范围,扩大了有状态类的变动频率,最后就造成软件系统的稳定性下降。
0 请登录后投票
   发表时间:2005-03-21  
robbin 写道

其实你的主张就是把持久化实体类的持久化操作附着到实体类上面去,而不是分开。举例来说,也就是说Account类的增删改查不应该单独分离到AccountDao接口去,而应该把增删改查操作放在Account类里面来完成,对不?


不是这个意思吧,firebody指的是除持久化的逻辑之外的业务逻辑放在实体类上去。并且,实体类搞出来之后应该脱离持久化的状态存在和被使用。只有在需要持久化的时候才做持久化的操作。
0 请登录后投票
   发表时间:2005-03-21  
Archie 写道
robbin 写道

其实你的主张就是把持久化实体类的持久化操作附着到实体类上面去,而不是分开。举例来说,也就是说Account类的增删改查不应该单独分离到AccountDao接口去,而应该把增删改查操作放在Account类里面来完成,对不?


不是这个意思吧,firebody指的是除持久化的逻辑之外的业务逻辑放在实体类上去。并且,实体类搞出来之后应该脱离持久化的状态存在和被使用。只有在需要持久化的时候才做持久化的操作。


如果是那样的话,那就更加糟糕,因为业务逻辑操作是依赖于DAO层的,而DAO层是依赖于实体类层的,如果你现在把业务逻辑操作都整合到实体类里面来的话,造成的结果就是实体类的更加不稳定,软件系统的代码相关性更高,有状态类波及的面积更大。
0 请登录后投票
   发表时间:2005-03-21  
robbin 写道
Archie 写道
robbin 写道

其实你的主张就是把持久化实体类的持久化操作附着到实体类上面去,而不是分开。举例来说,也就是说Account类的增删改查不应该单独分离到AccountDao接口去,而应该把增删改查操作放在Account类里面来完成,对不?


不是这个意思吧,firebody指的是除持久化的逻辑之外的业务逻辑放在实体类上去。并且,实体类搞出来之后应该脱离持久化的状态存在和被使用。只有在需要持久化的时候才做持久化的操作。


如果是那样的话,那就更加糟糕,因为业务逻辑操作是依赖于DAO层的,而DAO层是依赖于实体类层的,如果你现在把业务逻辑操作都整合到实体类里面来的话,造成的结果就是实体类的更加不稳定,软件系统的代码相关性更高,有状态类波及的面积更大。


业务逻辑操作为什么会依赖DAO层呢? 显然业务逻辑层应该在DAO层之上,应该是DAO层实现业务逻辑层的接口,依赖倒置啊。
0 请登录后投票
   发表时间:2005-03-21  
赞同robbin,理想的持久层,应该是依赖实体对象而独立的。

firebody的重心似乎是讨论由谁来调用持久层的问题。

持久层由实体类调用,不代表实体类依赖持久层(DIP),因此我想“而你把DAO操作合并到实体类以后,结果就是扩大了有状态类的代码相关性的范围和影响范围,扩大了有状态类的变动频率,最后就造成软件系统的稳定性下降”这一点感觉不完全成立。

另外,robbin提到“实体类是有状态的类”,从这个角度说,重点在于一个实体类持久化自己的状态是不是属于自己的职责。
我的看法是,单就持久化而言,如果考虑纯粹的面向对象分析,基本上会不考虑这一层,因此在实体对象模型的建设上应该对实体类完全透明;
在什么时间、地点如何持久化应该属于如何管理实体对象模型的问题,不应该属于实体类自己。
0 请登录后投票
   发表时间:2005-03-21  
你理解的业务逻辑和我说的不是一回事。还是拿前面例子来说吧,开立一个账户这叫做业务逻辑,针对数据库增加一条Account记录这叫做DAO操作,而Account这叫做实体类,他们的依赖关系如下:
引用

AccountManager(业务对象,属于业务层,表达业务逻辑操作,请注意事务控制是在这一层!) -->

AccountDao Interface(DAO接口,属于持久层,定义持久化操作方法,抽象ORM实现) -->

AccountDaoHibernateImpl(Hibernate的DAO实现,属于持久层,完成持久化工作,处理数据库资源管理,异常的捕获和转换) --->

Account(表达领域模型状态的实体类,属于持久层,这是唯一的有状态类,并且会被传递到业务层和Web层)


把AccountDao合并到Account里面去,这种方式Entity Bean就是这么做的,结果Martin Fowler仍然说Entity Bean是贫血的。所以现在你们主张把AccountManager也合并到Account里面,搞出来一个庞大的、多职责的粗颗粒度的类来。这样做带来的诸多缺陷前面已经详细分析,不再复述。
0 请登录后投票
论坛首页 Java企业应用版

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