锁定老帖子 主题:domain model的延伸讨论
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间: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显得有些臃肿,不过不需要手工写,可以忽略不计。 |
|
返回顶楼 | |
发表时间: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。 |
|
返回顶楼 | |
发表时间: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); //..... } } * 关于这个主题的争论已经反反复复进行了好几次,每次的结论都不一样,不过这样的过程确实反映了大家的认识都在变化和提高。 从这个主题的讨论中,似乎印证了这样的道理: 不管是黑猫白猫,只要能够捉住老鼠的就是好猫。 在注重敏捷实践的团队中,更应该捉住这个定理,当然,更应该捉住这个道理的对立面: 不管什么东西,只要是阻碍效率的,都应该审视并修正它。 |
|
返回顶楼 | |
发表时间:2007-03-03
要不把逻辑全部放实体对象里,要不就全部放servic里(除了那种一眼就觉得应该放实体的外)。不然感觉光琢磨这个该放那的问题,都会死掉好多脑细胞,而且还放不好。
|
|
返回顶楼 | |
发表时间:2007-03-03
见主贴内容
|
|
返回顶楼 | |
发表时间: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 |
|
返回顶楼 | |
发表时间: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) { 寻找这样的错误是没有意义的,只要思路正确,在座的各位都可以把代码调试好。 |
|
返回顶楼 | |
发表时间: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充血模型代码放在项目里面立刻可以跑的。至于你所谓的和其他层装配起来就可以跑的很欢畅,那么请你把你的代码和其他层装配好,然后作为附件上传,请大家来下载测试一下你的代码究竟能不能跑!你写了一个根本在实际项目中不能用的代码出来,能说明什么问题呢? |
|
返回顶楼 | |
发表时间:2007-03-03
这里是讨论问题,不是开发一个可运行的软件,没有人真的把这些代码下载放在项目里跑,也就看看思想罢了,总共就几十行代码,作为附件上传看起来也不方便。
|
|
返回顶楼 | |
发表时间:2007-03-03
见主贴贴
|
|
返回顶楼 | |