论坛首页 Java企业应用论坛

domain model的延伸讨论

浏览 73074 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-03-03  
对楼主的Java代码作一些修改,实现这个题目的Domain Model只需四个类,User,UserManager,Task,Department。
·不必每个Entity都安排一个Service,自身的业务功能可以直接放在Entity中;
·DAO不属Domain Model,Domain Model不涉及没有业务含义的技术细节,不需要框架的支持,跟Hibernate,Spring,AspectJ等都不沾边;
·添加task的操作放在User中;
·Task中不必持有owner,没必要的双向关联;
·Task是Value Object,不是Entity,不需要id,可以提高效率;
·DepartmentService改名为Department,里面的方法都是Department本身的功能。
·增加一个UserManager类。
//Entity
public class User {   
    private Long id;   
    private String name;   
    private String password;   
    private String gender;   
    private String department;   
    private int salary = 0;   
    private List<Task> tasks = new ArrayList<Task>();   
    # omit getter/setter methods ......   
    public int workload {   
        int totalDuration = 0;   
        for (Task task : tasks) {   
            totalDuration += task.duration;   
        }   
        return totalDuration;   
    } 
    public addTask(String taskName,int duration) {
        Task task=new Task();
        task.setName(taskName);
        task.setDuration(duration);
        tasks.add(task);
    }  
}

//Domain Service
public class UserManager {
    private UserDao userDao;   
    public void setUserDao(UserDao userDao) { this.userDao = userDao; } 
    public employee(String username, String department) {   
        User user = new User();   
        user.setName(username);   
        user.setDepartment(department);   
        userDao.addUser(user);   
    }
}

//Value Object
public class Task {
    private String name;   
    private int duration = 0;     
    # omit getter/setter methods ......   
}  

//Entity
public class Department {
    private Long id;   
    private String name;   
    # omit getter/setter methods ......   
    pirvate List users; 
    public int totalSalary(String department) {  
        int allSalary = 0;
        for (User user : users) {   
            allSalary += user.salary;   
        }   
        return allSalary;   
    }   
    public int totalWorkLoad(String department) {  
        int allWorkLoad = 0;   
        for (User user : users) {   
            allWorkLoad += userService.workload(user);   
        }   
        return allWorkLoad;   
    }       
} 

大部分的OO语言实现这样的模型都相差很小,因为其本身没有多少冗余,靠语法上的简洁性来来的简化微乎其微,相比之下,清晰和可阅读性更为重要一些。Java语言实现中只有getter/setter显得有些臃肿,不过不需要手工写,可以忽略不计。
0 请登录后投票
   发表时间:2007-03-03  
JavaInActoin 写道
对楼主的Java代码作一些修改,实现这个题目的Domain Model只需四个类,User,UserManager,Task,Department。
·不必每个Entity都安排一个Service,自身的业务功能可以直接放在Entity中;
·DAO不属Domain Model,Domain Model不涉及没有业务含义的技术细节,不需要框架的支持,跟Hibernate,Spring,AspectJ等都不沾边;
·添加task的操作放在User中;
·Task中不必持有owner,没必要的双向关联;
·Task是Value Object,不是Entity,不需要id,可以提高效率;
·DepartmentService改名为Department,里面的方法都是Department本身的功能。
·增加一个UserManager类。
//Entity
public class User {   
    private Long id;   
    private String name;   
    private String password;   
    private String gender;   
    private String department;   
    private int salary = 0;   
    private List<Task> tasks = new ArrayList<Task>();   
    # omit getter/setter methods ......   
    public int workload {   
        int totalDuration = 0;   
        for (Task task : tasks) {   
            totalDuration += task.duration;   
        }   
        return totalDuration;   
    } 
    public addTask(String taskName,int duration) {
        Task task=new Task();
        task.setName(taskName);
        task.setDuration(duration);
        tasks.add(task);
    }  
}

//Domain Service
public class UserManager {
    private UserDao userDao;   
    public void setUserDao(UserDao userDao) { this.userDao = userDao; } 
    public employee(String username, String department) {   
        User user = new User();   
        user.setName(username);   
        user.setDepartment(department);   
        userDao.addUser(user);   
    }
}

//Value Object
public class Task {
    private String name;   
    private int duration = 0;     
    # omit getter/setter methods ......   
}  

//Entity
public class Department {
    private Long id;   
    private String name;   
    # omit getter/setter methods ......   
    pirvate List users; 
    public int totalSalary(String department) {  
        int allSalary = 0;
        for (User user : users) {   
            allSalary += user.salary;   
        }   
        return allSalary;   
    }   
    public int totalWorkLoad(String department) {  
        int allWorkLoad = 0;   
        for (User user : users) {   
            allWorkLoad += userService.workload(user);   
        }   
        return allWorkLoad;   
    }       
} 

大部分的OO语言实现这样的模型都相差很小,因为其本身没有多少冗余,靠语法上的简洁性来来的简化微乎其微,相比之下,清晰和可阅读性更为重要一些。Java语言实现中只有getter/setter显得有些臃肿,不过不需要手工写,可以忽略不计。


你这个领域模型是根本跑不起来的。数据库根本就没有department这个表,你的department的users属性根本就是null,一跑就会出错。你的user.addTask方法也执行不了,没有Dao的支持,你再调用addTask,数据库里面也不增加记录。UserManager也不应该存在,这是属于user的domain logic。




0 请登录后投票
   发表时间:2007-03-03  
使用Java ORM的Model 的模型虽然没有ROR里面的ActiveRecord“使用得那么爽快“,

但是仍然具备它独特的优点,我谈谈我得几点看法:

* 它不能称为贫血模型。 它本身也可以建立 “和它自身状态紧密相连“的领域逻辑。

  这里我加了一个前提:和它自身状态紧密相连

  因为现有框架的Java ORM 模型基本上都不支持在entity中注入DAO/Service这样的component.

  比如这样的一个类:

  Department{   }

  如果没有一个一对多的关联: Deparment.members ,就不能直接在这个entity上实现一个业务逻辑:计算部门所有员工的工资总和。

  为此需要把 List<Member> members 作为Department的一个“内部状态“,这样,“计算部门员工工资总和“的逻辑
  也自然而然的“被划分“到 Department身上。


  当然,这也存在反面的地方,也就是潜在的一个语义: 所有的划分到某个Entity身上的业务逻辑都需要确立应该引入“哪些
  关系“作为当前实体的一种内部状态。

  这样强的限制 让很多 喜欢 “随意“划分 业务逻辑到“实体“模型上的人 很不爽。

  同样,我也感觉不爽。不过还在可以“接受“的范围。


*  每个Service对应一个 DAO 这样的经典设计 未必总是那么适用。

    或许更合适的使用 Java ORM的设计 应该是 一个通用的 DAO ,所有的Service 继承自这个DAO, 在通用的DAO提供通用的CRUD的操作。

依据JDK5.0的范性,   这个设计获得的好处可能要比想象的还要来的多。(SpringSide似乎就是推崇这样的做法)


    当然,可能失去了 Service中 MockDAO的特性。
    但是,有没有 想过 完全采用“ 抛弃MockDAO,直接准备测试数据“的方式来测试依赖于底层数据库状态的Service逻辑呢?
   
   
*   完全依赖于IOC容器的方式,Service的获得 是一种非常通用而且简易的方式。


*  虽然很多业务逻辑划入实体模型很合理,但是也有很多逻辑似乎并不总是属于某个特定的实体。

  ***Manager,***Handler,****Helper,甚至****Service更合适一些。

  如果引入了这些类似于Service的业务逻辑类,那么从这个角度来看,这些service 和 entities共同组成了完成业务
  逻辑的领域模型。

  当然,因为现有框架的设计,java项目里面, 为实现需求,我们直接面对的总是从Service开始,测试也总是从Service开始。然而,这并不能阻止
  设计人员把合适的业务逻辑 放到 实体模型里面去。
 

* 至于怎么在java里面实现充血模型。我记得很久以前有过这样的想法:

       abstract class Entity {
Entity(){
ContextLocator.getRegisteredContext().autowire(this);
}
   
}



public  Department extends Entity{

           private DepartmentDao departmentDao;
  
   public int calTotalSalary(){
              List<member) members = departmentDao.findMembersByDepartmentId(this.id);
      //.....
   }
}


* 关于这个主题的争论已经反反复复进行了好几次,每次的结论都不一样,不过这样的过程确实反映了大家的认识都在变化和提高。

  从这个主题的讨论中,似乎印证了这样的道理:

     不管是黑猫白猫,只要能够捉住老鼠的就是好猫。

  在注重敏捷实践的团队中,更应该捉住这个定理,当然,更应该捉住这个道理的对立面:

不管什么东西,只要是阻碍效率的,都应该审视并修正它。
0 请登录后投票
   发表时间:2007-03-03  
要不把逻辑全部放实体对象里,要不就全部放servic里(除了那种一眼就觉得应该放实体的外)。不然感觉光琢磨这个该放那的问题,都会死掉好多脑细胞,而且还放不好。
0 请登录后投票
   发表时间:2007-03-03  
见主贴内容
0 请登录后投票
   发表时间:2007-03-03  
complystill 写道
另外从Robbin的AR的代码中看不出级联删除的实现, TOB的模型中因为这个判断是应用重载 Tie.killingTargetNotify() 来实现的, 其中可以进行任意复杂的判断, 决定是删是留, 或者做其它处理. 这个地方如果不重载, 默认的处理在一个 Department 被删除后, 它所有的 Employee的 department 引用都会被清空. 删Employee时Task的处理类似.

不知道这个灵活性在AR中是怎么支持的.


class User < ActiveRecord::Base
  has_many :tasks, :dependent => :destroy
end
0 请登录后投票
   发表时间:2007-03-03  
robbin 写道

你这个领域模型是根本跑不起来的。数据库根本就没有department这个表,你的department的users属性根本就是null,一跑就会出错。你的user.addTask方法也执行不了,没有Dao的支持,你再调用addTask,数据库里面也不增加记录。UserManager也不应该存在,这是属于user的domain logic。
请不要拿一个根本不能运行的错误代码出来,请先在你本地搭建一个实际的web项目,自己测试通过了,再贴出来。

单独的领域模型当然跑不起来,和其它层装配起来就跑的很以欢畅了。
我写的Domain Model是根据Eric Evans的观点来的,很多持久化(包括事务)的操作超越了业务逻辑,需要被安排在应用服务层。
你写的那段不是MF的充血模型,而是事务脚本。
robbin 写道

请不要拿一个根本不能运行的错误代码出来,请先在你本地搭建一个实际的web项目,自己测试通过了,再贴出来。

这个不是讨论问题的最佳方法,太累了,你写的代码同样不能运行
robbin 写道

    public employee(String username, String department) {   


寻找这样的错误是没有意义的,只要思路正确,在座的各位都可以把代码调试好。
0 请登录后投票
   发表时间:2007-03-03  
JavaInActoin 写道
robbin 写道

你这个领域模型是根本跑不起来的。数据库根本就没有department这个表,你的department的users属性根本就是null,一跑就会出错。你的user.addTask方法也执行不了,没有Dao的支持,你再调用addTask,数据库里面也不增加记录。UserManager也不应该存在,这是属于user的domain logic。
请不要拿一个根本不能运行的错误代码出来,请先在你本地搭建一个实际的web项目,自己测试通过了,再贴出来。

单独的领域模型当然跑不起来,和其它层装配起来就跑的很以欢畅了。
我写的Domain Model是根据Eric Evans的观点来的,很多持久化(包括事务)的操作超越了业务逻辑,需要被安排在应用服务层。
你写的那段不是MF的充血模型,而是事务脚本。
robbin 写道

请不要拿一个根本不能运行的错误代码出来,请先在你本地搭建一个实际的web项目,自己测试通过了,再贴出来。

这个不是讨论问题的最佳方法,太累了,你写的代码同样不能运行
robbin 写道

    public employee(String username, String department) {   


寻找这样的错误是没有意义的,只要思路正确,在座的各位都可以把代码调试好。


我写的RoR充血模型代码放在项目里面立刻可以跑的。至于你所谓的和其他层装配起来就可以跑的很欢畅,那么请你把你的代码和其他层装配好,然后作为附件上传,请大家来下载测试一下你的代码究竟能不能跑!你写了一个根本在实际项目中不能用的代码出来,能说明什么问题呢?
0 请登录后投票
   发表时间:2007-03-03  
这里是讨论问题,不是开发一个可运行的软件,没有人真的把这些代码下载放在项目里跑,也就看看思想罢了,总共就几十行代码,作为附件上传看起来也不方便。
0 请登录后投票
   发表时间:2007-03-03  
见主贴贴
0 请登录后投票
论坛首页 Java企业应用版

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