论坛首页 Java企业应用论坛

谈一谈贫血的Domain Logic问题。

浏览 42410 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-20  
如今采用Hibernate实现的Domain Model,多数只是维护实体之间的关联,而大多数的业务逻辑,则是由Service Layer来实现。

这样的模型对象拥有的行为太少了,以至于Martin Fowler给他们下了一个定义:贫血模型。

我们知道,高内聚低耦合是衡量一个模型设计是否合理的重要标准之一。对象组件间合理分工协作可以解决复杂的问题逻辑,按照这个标准,我们似乎可以很自然的各种行为封装到各个模型对象中。 然而,现在绝大多数的应用没有这样做。

ORM作为模型对象与数据库模型之间的接口,它的引入无疑承担了实体领域模型所能称之为领域模型的 所有责任。 正如同Martin Fowler所说的,贫血的领域模型承担了领域模型的所有成本,却没有得到真正的收益。
在这里,真正的收益应该是指高内聚低耦合的拥有复杂对象行为的领域模型,确实,我们设计的领域模型根本没有实现任何的功能,我们只能在外面从新设计一个 Service Layer来管理所有的行为。

我不敢评论这样的设计方案是怎样的不合理。 当设计到拥有比较复杂问题领域模型的时候,这种只负责管理实体间关联关系的实体模型肯定不能适应,这样做的后果就是将复杂领域逻辑统统 移植到 Service Layer层或者胡乱给起名字的一个外层。

考虑Martin Fowler 《Analysis Patterns》中著名的一个通用模型:团体责任模型。里面的约束需要在实体领域模型中得以实现,在贫血领域模型中,封装实现这样的需要检索 验证某个甚至全部实体数据的行为只能移植到Service Layer中。 这样的移植对于领域模型的构架无疑大大增加了复杂度。


那么,我们能不能在贫血领域模型基础上,加入对象行为,使之拥有丰富的行为呢? 我想这是可以解决的,解决的关键是将可访问底层实体数据的行为赋予每一个实体模型对象,最简便的办法就是用一个全局访问点来实现。

考虑这么一个层次:
public interface ServiceProvider{
                public Object getService(String serviceName);;
       
}


public ServiceProviderImpl{
              public ObjectgetService(String serviceName);{
             
                    return ServiceLocator.getService(serbiceName  );;
             }
               
}

public interface CRUD{
     public void save();;
      public void delete();;
       public void load(Long id);;
      public void update();;
}

public Group implements CRUD {

    private String name;
     private List  users;
      public GroupService getGroupService();{
           return (GroupService);getServiceProvider();.getService(this.class.getName();+"Service");
      }
     public void save();{
          if(getGroupService();.findGroupByName(name);!=null);
                       throw new RuntimeExepion("duplicate group name!");;
          getGroupService();.save(this);;
      }
      public  Group load(Long id);{
           this=getGroupService();.load(this.class,id);;
           return this;
      }
      
     public void addUser(user user);{
             
            users.add(user);;
            this.save();; 
     
    }
     public void removeUser(User user);{
     }

     

}


这样作的问题是与建立贫血对象模型相比,领域对象模型的行为通用需要ServiceLayer来完成,约定:
1)ServiceLayer层只负责实现简单的单步骤的与底层数据库访问的 逻辑,不包含任何业务领域逻辑。 如上面的 service.save(),service.update, service.delete , service.findGroupByName.... 

2) 领域模型对象负责对自身的领域逻辑进行封装。

3)通过赋予模型对象行为,建立对象间行为关联,以完成更复杂的 商业逻辑。

4)外层业务逻辑层只能看到领域模型对象,不能直接操作任何的类似Service.save这样的直接访问底层数据库的行为。
   发表时间:2005-03-20  
个人感觉:贫血的Domain Model之说,是乱扣大帽子。

例子1、银行帐号Account:
一个经常引起争论的问题就是,deposit/draw方法到底应该建模到 Account中还是建模到AccountManager(对一个银行出纳员的建模)中。
我觉的将deposit/draw建模到AccountManger中是反映现实的,因为Account反映到现实世界就是一个没有行为能力的实体,而出纳员才具有行为能力。
例子2:选课的学生Student
对学生建模的时候,enroll(选课行为)毫无疑问应该建模到类Student中。
除此之外,我们假设每个学生有一个考察级点,可以根据这个学生的表现增加或者减少级点,而我们就遇到同例1中提到的deposit/draw一样的问题,就是增加级点的方法addScore/减少级点的方法reduceScore建模到Student中还是StudentManager中,个人认为,同于例1,应该建模到StudentManager中而不是Student中。

总的来说,方法到底建模到那个Class中,就根据这个方法是否是这个Class自发的行为。譬如,Account的deposit/draw就不应该建模到Account中,但是实践中,建模到Account中,往往会带来一些好处,至于什么好处,Martin的《Domain Logic and SQL》非常全面的分析过了。所以,我感觉,Martin举的例子中,贫血的Domain Model是乱扣大帽子。

本来就没什么Transaction Script,没什么贫血的Domain Model,Martin非要将一种正确的模型叫做“贫血的Domain Model",而将没有反映现实世界的、但是有一定实践价值的模型起一个风度翩翩的名字。

为什么有人经常提到贫血的Domain Model?我觉的,可能很多应用是以数据为中心的,所以,建模的时候,习惯性地思维定向到数据上,建模出来的类完全是DB Entity的反映。而正确的思路,应该针对整个系统进行对象建模,不要一开始就考虑持久化的问题。
0 请登录后投票
   发表时间:2005-03-20  
引用
例子1、银行帐号Account:
一个经常引起争论的问题就是,deposit/draw方法到底应该建模到 Account中还是建模到AccountManager(对一个银行出纳员的建模)中。
我觉的将deposit/draw建模到AccountManger中是反映现实的,因为Account反映到现实世界就是一个没有行为能力的实体,而出纳员才具有行为能力。

我认为你对Domain Logic存在理解偏差。
从Domain logic角度来看,如果Account维护着Balance信息,那么将deposit/draw方法建模到 Account 是毫无疑问的。 对于Account 来说,存取款操作的是它的余额,毫无疑问,这个领域逻辑应该归属到这个对象上。
0 请登录后投票
   发表时间:2005-03-20  
firebody 写道
引用
例子1、银行帐号Account:
一个经常引起争论的问题就是,deposit/draw方法到底应该建模到 Account中还是建模到AccountManager(对一个银行出纳员的建模)中。
我觉的将deposit/draw建模到AccountManger中是反映现实的,因为Account反映到现实世界就是一个没有行为能力的实体,而出纳员才具有行为能力。

我认为你对Domain Logic存在理解偏差。
从Domain logic角度来看,如果Account维护着Balance信息,那么将deposit/draw方法建模到 Account 是毫无疑问的。 对于Account 来说,存取款操作的是它的余额,毫无疑问,这个领域逻辑应该归属到这个对象上。

同意firebody。
deposit/draw应该建立在Account上,因为这是面向对象的本质决定的。如果将对自己的数据的操作,交给另外一个对象来完成,这个封装是失败的,不是面向对象的设计。贫血模型讨论的实际上是面向对象,还是面向过程的设计的问题。如果将deposit/draw建模到AccountManager上实际上是一个打着面向对象的幌子的面向过程设计,我想这也是Martin想表达的意思。
0 请登录后投票
   发表时间:2005-03-21  
firebody 写道
如今采用Hibernate实现的Domain Model,多数只是维护实体之间的关联,而大多数的业务逻辑,则是由Service Layer来实现。

这样的模型对象拥有的行为太少了,以至于Martin Fowler给他们下了一个定义:贫血模型。


我觉得firebody从开始就犯了个错误,就是把Hibernate维护的PO看作是领域模型对象

service layer实现业务逻辑这个是说对了
但是Hibernate实现Domain Model这种方式实际上Fowler和Larman等人也没有这么说

PO维护静态关联是有实体或对象属性决定的
Domain Model的操作则是由业务逻辑决定的,也就是service Layer反映出来的功能,service layer本来就不是面向对象的,不是所有的东西都是面向对象的,面向对象一说是指的service layer下面的Domain Model实体

firebody 写道
那么,我们能不能在贫血领域模型基础上,加入对象行为,使之拥有丰富的行为呢?


如果是这样的话,我觉得不如说能否在拥有行为的Domain Model上加入数据持久话操作,以便获得数据关联与持久化能力

以上看法也许因为我跟firebody设计习惯不同,但是service layer不应该被拉入到面向对象这一说法中是我的基本观点

最后多说一句:大家对Domain Model的热情,使得它似乎快成了javaeye的月经贴了,每个月都来那么一次
0 请登录后投票
   发表时间:2005-03-21  
我的理解是Martin大叔所谓的domain object是“领域模型”,它是一个针对业务逻辑抽象的模型,和软件编程根本毫无关系。即使你不开发这个软件,你仍然需要抽象你的业务逻辑得到你的领域模型。这个领域指的是你所从事的行业,而不是狭隘的持久化类

并且领域模型的建模也是在需求分析阶段,或者在需求分析阶段之前完成的事情。具体到编程的过程中,领域模型并非对应某一个Java类,如果一定要强行对应的话,(业务对象,Dao接口,Hibernate实体类)他们合起来统称领域模型在Java语言的实现。把 商业建模范畴的“领域模型”拿来当做Hibernate编程中的实体类,根本就是牛头不对马嘴!
0 请登录后投票
   发表时间:2005-03-21  
robbin 写道
我的理解是Martin大叔所谓的domain object是“领域模型”,它是一个针对业务逻辑抽象的模型,和软件编程根本毫无关系。即使你不开发这个软件,你仍然需要抽象你的业务逻辑得到你的领域模型。这个领域指的是你所从事的行业,而不是狭隘的持久化类

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


把商业建模范畴的“领域模型”拿来做实体类,是一种最容易让人选择方法。我是常常都这么做了,做的过程中,再逐渐的为实体类加入业务逻辑,感觉还是经验不足啊。
0 请登录后投票
   发表时间:2005-03-21  
Archie 写道
robbin 写道
我的理解是Martin大叔所谓的domain object是“领域模型”,它是一个针对业务逻辑抽象的模型,和软件编程根本毫无关系。即使你不开发这个软件,你仍然需要抽象你的业务逻辑得到你的领域模型。这个领域指的是你所从事的行业,而不是狭隘的持久化类

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


把商业建模范畴的“领域模型”拿来做实体类,是一种最容易让人选择方法。我是常常都这么做了,做的过程中,再逐渐的为实体类加入业务逻辑,感觉还是经验不足啊。


商业建模范畴的“领域模型”你拿来编程的话,他的映射关系不是1:1的,而是1:n的,也就是说一个抽象的领域模型它应该得到的是一套Java分层的类,包括Hibernate实体类,Dao接口,业务对象等等一套类,而不是仅仅对应一个Hibernate实体类。
0 请登录后投票
   发表时间:2005-03-21  
robbin 写道

商业建模范畴的“领域模型”你拿来编程的话,他的映射关系不是1:1的,而是1:n的


说着说着好像又出现AOP的意思了
0 请登录后投票
   发表时间:2005-03-21  
flyingbug 写道
robbin 写道

商业建模范畴的“领域模型”你拿来编程的话,他的映射关系不是1:1的,而是1:n的


说着说着好像又出现AOP的意思了


和AOP有什么关系? 你说说看!
0 请登录后投票
论坛首页 Java企业应用版

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