- 浏览: 4833385 次
- 性别:
- 来自: 上海
-
博客专栏
-
-
robbin谈管理
浏览量:137874
文章分类
最新评论
-
xly1981:
领导者是团队的灵魂。深入一线的过程,包括代码review,能帮 ...
robbin谈管理:改造团队的经验(2) -
jiehuangwei:
像这种总结比较性的ppt文档可以多发啊
Web并发模型粗浅探讨 -
linux1308:
看完学习到了很多东西,感谢推荐!
推荐一篇很好的RoR部署方案性能评测 -
zweite:
直接对搜索的结果进行缓存是不是会更快一点呢
漫谈应用缓存的命中率问题 -
kaogua:
现在已经是ruby2.0了, 不知道这个的效率是怎么样的, 是 ...
Ruby作为服务器端应用已经成熟了
domain model,又称为领域模型,是Java企业应用讨论的一个热门话题,JavaEye也曾经多次围绕这个话题讨论,我们来看个简单的例子:
一个简单的公司工时管理系统,记录员工的个人信息,每个员工的工作任务分配,以及工作所属类别(例如开发,还是测试,还是培训等等),其中每个员工有n个任务,员工和任务是一对多关系,每个员工也分别隶属于多个不同的工作类别,员工和类型是多对多关联关系,而每个任务也分别隶属于唯一的工作类别,任务和类别是多对一关系。另外系统不要求对部门信息进行维护,不需要department表。因此,在这个系统中使用四张数据库表:
users表保存员工信息,有name, password, gender, department, salary
tasks表保存工作任务信息,有name,start_time, end_time
kinds表保存工作所属类别,有name
kinds_users表是一张关联表,保存users表和kinds表的多对多关联外键的
系统的功能需求如下:
1、某部门录用一名新员工
2、某部门员工总薪水总和
3、某员工已经开始但尚未结束的任务
4、给某员工分配一项任务
5、所有用户当前已经开始但尚未结束的任务
6、对某一类别,给所有和此一类别相关的员工,批量新增一批任务
7、针对任务的统计功能,给定某类别,统计当月总的任务数,已完成任务数,未完成任务数
我们先看看用ruby如何实现系统的领域模型:
1、某部门录用一名新员工
2、某部门员工总薪水总和
3、某员工已经开始但尚未结束的任务
4、给某员工分配一项任务
5、所有用户当前已经开始但尚未结束的任务
6、对某一类别,给所有和此一类别相关的员工,批量新增一批任务
7、针对任务的统计功能,给定某类别,统计当月总的任务数,已完成任务数,未完成任务数
这里值得注意的是,RoR可以很方便的采用充血的领域模型,所有的业务逻辑都可以放在相关的domain model里面。这里的user,task和kind都是对应于数据库表的领域模型,而department是不对应数据库的纯业务逻辑的domain model。总共4个ruby文件,4个domain model,55行代码,所有要写的代码都在这里了,代码量确实非常少,每个domain model的颗粒度都比较大。
然后我们再看看如何用Java:
Java版本的实现代码大家都比较熟悉,因此绝大部分代码都省略了。Java版本需要3个持久对象,3个映射XML文件,3个DAO接口和实现类,4个Service和实现类,和一个IoC的bean组装文件,总共21个文件,全部逻辑写完整,代码行数至少上千行。
通过对比,我们可以看到Java比较流行的实现是贫血的模型,按照面向对象的基本原则,对象的状态应该和它的行为封装在一起,因此Java多出来的这些XXXService是一些从纯理论角度而言应该放入其相应的持久对象中去。但是Java实现充血模型从技术上有一定的难度,如何Service方法挪入到持久对象中呢?如何解决Dao的注入问题?如何解决domain logic方法的事务封装问题?前者可以通过AspectJ的静态织入来解决,后者也许可以通过织入或者annotation声明来解决。但不管怎么说,Java从技术上很难实现充血模型,而且即使实现充血模型,也会导致一个Java类好几百行代码的状况,其代码的可阅读性,模块解藕能力都会变得很差,因此我们认为Java不适合充血模型,在表达复杂的业务逻辑的能力上,Java要比ruby差很多:
结论:
对于Java来说,更加适合采用贫血的模型,Java比较适合于把一个复杂的业务逻辑分离到n个小对象中去,每个小对象描述单一的职责,n个对象互相协作来表达一个复杂的业务逻辑,这n个对象之间的依赖和协作需要通过外部的容器例如IoC来显式的管理。但对于每个具体的对象来说,他们毫无疑问是贫血的。
这种贫血的模型好处是:
1、每个贫血对象职责单一,所以模块解藕程度很高,有利于错误的隔离。
2、非常重要的是,这种模型非常适合于软件外包和大规模软件团队的协作。每个编程个体只需要负责单一职责的小对象模块编写,不会互相影响。
贫血模型的坏处是:
1、由于对象状态和行为分离,所以一个完整的业务逻辑的描述不能够在一个类当中完成,而是一组互相协作的类共同完成的。因此可复用的颗粒度比较小,代码量膨胀的很厉害,最重要的是业务逻辑的描述能力比较差,一个稍微复杂的业务逻辑,就需要太多类和太多代码去表达(针对我们假定的这个简单的工时管理系统的业务逻辑实现,ruby使用了50行代码,但Java至少要上千行代码)。
2、对象协作依赖于外部容器的组装,因此裸写代码是不可能的了,必须借助于外部的IoC容器。
对于Ruby来说,更加适合充血模型。因为ruby语言的表达能力非常强大,现在用ruby做企业应用的DSL是一个很热门的领域,DSL说白了就是用来描述某个行业业务逻辑的专用语言。
充血模型的好处是:
1、对象自洽程度很高,表达能力很强,因此非常适合于复杂的企业业务逻辑的实现,以及可复用程度比较高。
2、不必依赖外部容器的组装,所以RoR没有IoC的概念。
充血模型的坏处是:
1、对象高度自洽的结果是不利于大规模团队分工协作。一个编程个体至少要完成一个完整业务逻辑的功能。对于单个完整业务逻辑,无法再细分下去了。
2、随着业务逻辑的变动,领域模型可能会处于比较频繁的变动状态中,领域模型不够稳定也会带来web层代码频繁变动。
附件是完整的RoR版本的项目示例代码。要运行它,需要安装MySQL数据库(InnoDB表类型),Ruby和Ruby on rails环境。在MySQL数据库中分别创建demo数据库和demo_test数据库,修改demo\config\database.yml中的MySQL数据库配置,改成你的数据库密码。然后在项目跟目录下面执行:
rake db:migrate
rake db:test:clone_structure
rake test
即创建开发环境数据库,创建测试环境数据库,和执行所有的单元测试。领域模型代码位于demo\app\models目录下面;单元测试代码位于demo\test\units目录下面
你这个领域模型是根本跑不起来的。数据库根本就没有department这个表,你的department的users属性根本就是null,一跑就会出错。你的user.addTask方法也执行不了,没有Dao的支持,你再调用addTask,数据库里面也不增加记录。UserManager也不应该存在,这是属于user的domain logic。
如果这个UserList是一个Department的所有成员列表, 那么这个List本身是不是可以认为是Department的一部分呢?
其实这是关系模型真正的好处, 你指定了一个 User 的 Dep 是xx, 那么那个Dep的成员列表在逻辑上就直接包含了这个User. TOB是把这种关系在系统级别自动实现了, addUser到一个Dep的memberList是直接依赖于这个User的dep属性的, 如果必须自己维护保障其一致性, 对持久应用来说应该是一种负担吧.
网络模型是从Dep用一个列表引用User, 而关系模型是设定User的Dep属性 (传统上称为Foreign Key, 而在TOB中发展成Tie引用).
我写这个例子不是想比较代码行数的。否则我就不会omit那么多信息了。我想说明的无非就是两点:
1、充血模型RoR实现起来很简单,而Java可以实现但有技术上的难度
2、RoR更加适合采用充血模型,而其复杂业务逻辑描述能力强于Java
只不过某些人硬是说Java也可以很方便的充血,而且RoR只能做简单web项目,硬说复杂企业业务逻辑Java描述能力更强。所以我在等这些人把代码给我贴出来,我好继续讨论下去。
Well,我也并没有在揣测你的意图。
对我来说,LOC makes sense。假如这个例子在Java中能用56行写完rich model(两倍于Ruby,够宽容了),我相信Java中没有理由不用rich model。
正所谓“仁者见仁,智者见智”,在你看来别人心里也是有你那么个等式。Ruby和Java就是技术上的区别,别人谈的是技术上的区别,我看到的也是技术上的区别,到你这就不是技术上的区别了。
就Robbin的例子,你做个Java的Rich Domain Model要多少行程序?Rails用多少行程序?
我写这个例子不是想比较代码行数的。否则我就不会omit那么多信息了。我想说明的无非就是两点:
1、充血模型RoR实现起来很简单,而Java可以实现但有技术上的难度
2、RoR更加适合采用充血模型,而其复杂业务逻辑描述能力强于Java
只不过某些人硬是说Java也可以很方便的充血,而且RoR只能做简单web项目,硬说复杂企业业务逻辑Java描述能力更强。所以我在等这些人把代码给我贴出来,我好继续讨论下去。
Martin Fowler在PoEAA中认为,越是企业应用的复杂业务逻辑,Rich domain model架构越合适,对于简单企业应用,贫血模型也不错,但是他认为随着业务逻辑复杂度上升,贫血模型的弊端会被放大,变得开始不合适了。
我的立论是:
其实即便在上一个讨论贴中,我都从来没有说过贫血模型就不好,充血模型就好这样的话。我只说过Java要实现充血模型有技术上的难度,而RoR原生支持充血模型,由于充血模型更适合于企业复杂的业务逻辑,因此RoR理论上要比Java描述复杂企业业务逻辑的能力更强,更加适合业务逻辑复杂的企业应用项目的开发。
但请注意我从来没有说贫血模型就不好,充血模型就一定好的话。其实模型无所谓好坏,各自有不同的合适的应用场景罢了。所以你非要和我争,说对象状态和行为分离也没有什么不好,贫血也没有什么不好,Martin Fowler说的也不一定就是对的,那靶子就歪了。是的,我完全同意你的观点,但我的帖子讲的不是这个话题,我的帖子不是探讨哪个模型好哪个模型不好的,我的帖子是探讨Java和RoR哪个比较适合充血模型,哪个比较合适复杂业务逻辑的描述的。
UserService本身就是针对user的一个抽象而已,不考虑我们现在的spring等的技术,也不考虑dao的注入,用个jdbc,把user的功能和userservice 里面的方法放一起实现也不是不可以?
你可以不用spring/hiberaten,可以只用JDBC,不妨把你只用JDBC的代码贴出来,我等这个已经等了很久了。n个人回贴说如果不要XX,不要XX,Java也可以充血模型,但是还没有一个人把代码给我贴出来证明给我看。
正所谓“仁者见仁,智者见智”,在你看来别人心里也是有你那么个等式。Ruby和Java就是技术上的区别,别人谈的是技术上的区别,我看到的也是技术上的区别,到你这就不是技术上的区别了。
就Robbin的例子,你做个Java的Rich Domain Model要多少行程序?Rails用多少行程序?
引用
一个简单的公司工时管理系统,记录员工的个人信息,每个员工的工作任务分配,以及工作所属类别(例如开发,还是测试,还是培训等等),其中每个员工有n个任务,员工和任务是一对多关系,每个员工也分别隶属于多个不同的工作类别,员工和类型是多对多关联关系,而每个任务也分别隶属于唯一的工作类别,任务和类别是多对一关系。另外系统不要求对部门信息进行维护,不需要department表。因此,在这个系统中使用四张数据库表:
users表保存员工信息,有name, password, gender, department, salary
tasks表保存工作任务信息,有name,start_time, end_time
kinds表保存工作所属类别,有name
kinds_users表是一张关联表,保存users表和kinds表的多对多关联外键的
系统的功能需求如下:
1、某部门录用一名新员工
2、某部门员工总薪水总和
3、某员工已经开始但尚未结束的任务
4、给某员工分配一项任务
5、所有用户当前已经开始但尚未结束的任务
6、对某一类别,给所有和此一类别相关的员工,批量新增一批任务
7、针对任务的统计功能,给定某类别,统计当月总的任务数,已完成任务数,未完成任务数
我们先看看用ruby如何实现系统的领域模型:
class User < ActiveRecord::Base has_and_belongs_to_many :kinds has_many :tasks, :dependent => :destroy do def processing_tasks find :all, :conditions => ["start_time <= ? AND end_time is null", Time.now] end end def apply_task(task_name) self.tasks << Task.new(:name => task_name, :start_time => Date.today) end def self.all_processing_tasks Task.find :all, :conditions => ["start_time <= ? AND end_time is null AND user_id is not null",Time.now] end end class Task < ActiveRecord::Base belongs_to : owner, :class_name => 'User', :foreign_key => 'user_id' belongs_to :kind def self.current_month_tasks(kind) kind.tasks.current_month_tasks end end class Kind < ActiveRecord::Base has_and_belongs_to_many :users has_many :tasks do def current_month_tasks month_begin = Date.today - Date.today.mday + 1 month_end = Date.today - Date.today.mday + 30 processing_tasks = find :all, :conditions => ["start_time <= ? AND end_time is null ", month_begin] processed_tasks = find :all, :conditions => ["end_time >= ? AND end_time <= ? ", month_begin, month_end] all_tasks = processing_tasks.clone all_tasks << processed_tasks unless processed_tasks.size == 0 return all_tasks, processed_tasks, processing_tasks end end def add_batch_task_to_users(task_name) self.users.each do |user| task = Task.new(:name => task_name, :start_time => Date.today) user.tasks << task self.tasks << task end end end class Department def self.employee(username, department) User.create(:name => username, :department => department) end def self.total_salary(department) User.sum :salary, :conditions => ["department = ?", department] end end
1、某部门录用一名新员工
Department.employee("robbin","开发部")
2、某部门员工总薪水总和
Department.total_salary("开发部")
3、某员工已经开始但尚未结束的任务
user.tasks.processing_tasks
4、给某员工分配一项任务
user.apply_task("学习Java")
5、所有用户当前已经开始但尚未结束的任务
User.all_processing_tasks
6、对某一类别,给所有和此一类别相关的员工,批量新增一批任务
kind.add_batch_task_to_users("学习单元测试")
7、针对任务的统计功能,给定某类别,统计当月总的任务数,已完成任务数,未完成任务数
Task.current_month_tasks(kind)
这里值得注意的是,RoR可以很方便的采用充血的领域模型,所有的业务逻辑都可以放在相关的domain model里面。这里的user,task和kind都是对应于数据库表的领域模型,而department是不对应数据库的纯业务逻辑的domain model。总共4个ruby文件,4个domain model,55行代码,所有要写的代码都在这里了,代码量确实非常少,每个domain model的颗粒度都比较大。
然后我们再看看如何用Java:
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 ...... } # omit User's ORM Mapping file public class Task { private Long id; private String name; private int duration = 0; private User owner; # omit getter/setter methods ...... } # omit Task's ORM Mapping file public class Kind { ...... } # omit Kind's ORM Mapping file public interface UserDao { public void addUser(User user); public loadUserById(Long id); # omit CRUD and other persistent methods ...... public List<User> findByDeparment(String department); } public interface TaskDao { # omit CRUD and other persistent methods ...... } public class UserDaoImpl { # omit implementations ...... } public class TaskDaoImpl { # omit implementations ...... } public class UserService { private UserDao userDao; public setUserDao(UserDao userDao) { this.userDao = userDao; } public int workload(User user) { int totalDuration = 0; for (Task task : user.getTasks()) { totalDuration += task.duration; } return totalDuration; } public employee(String username, String department) { User user = new User(); user.setName(username); user.setDepartment(department); userDao.addUser(user); } } public class TaskService { private TaskDao taskDao; public void setTaskDao(TaskDao taskDao) { this.taskDao = taskDao } public applyTask(String taskName, User user) { Task task = new Task(); task.setName(taskName); task.setUser(user); taskDao.addTask(task); } } public class DepartmentService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public int totalSalary(String department) { ...... } ...... } # omit IoC Container weaving configuration's file
Java版本的实现代码大家都比较熟悉,因此绝大部分代码都省略了。Java版本需要3个持久对象,3个映射XML文件,3个DAO接口和实现类,4个Service和实现类,和一个IoC的bean组装文件,总共21个文件,全部逻辑写完整,代码行数至少上千行。
通过对比,我们可以看到Java比较流行的实现是贫血的模型,按照面向对象的基本原则,对象的状态应该和它的行为封装在一起,因此Java多出来的这些XXXService是一些从纯理论角度而言应该放入其相应的持久对象中去。但是Java实现充血模型从技术上有一定的难度,如何Service方法挪入到持久对象中呢?如何解决Dao的注入问题?如何解决domain logic方法的事务封装问题?前者可以通过AspectJ的静态织入来解决,后者也许可以通过织入或者annotation声明来解决。但不管怎么说,Java从技术上很难实现充血模型,而且即使实现充血模型,也会导致一个Java类好几百行代码的状况,其代码的可阅读性,模块解藕能力都会变得很差,因此我们认为Java不适合充血模型,在表达复杂的业务逻辑的能力上,Java要比ruby差很多:
结论:
对于Java来说,更加适合采用贫血的模型,Java比较适合于把一个复杂的业务逻辑分离到n个小对象中去,每个小对象描述单一的职责,n个对象互相协作来表达一个复杂的业务逻辑,这n个对象之间的依赖和协作需要通过外部的容器例如IoC来显式的管理。但对于每个具体的对象来说,他们毫无疑问是贫血的。
这种贫血的模型好处是:
1、每个贫血对象职责单一,所以模块解藕程度很高,有利于错误的隔离。
2、非常重要的是,这种模型非常适合于软件外包和大规模软件团队的协作。每个编程个体只需要负责单一职责的小对象模块编写,不会互相影响。
贫血模型的坏处是:
1、由于对象状态和行为分离,所以一个完整的业务逻辑的描述不能够在一个类当中完成,而是一组互相协作的类共同完成的。因此可复用的颗粒度比较小,代码量膨胀的很厉害,最重要的是业务逻辑的描述能力比较差,一个稍微复杂的业务逻辑,就需要太多类和太多代码去表达(针对我们假定的这个简单的工时管理系统的业务逻辑实现,ruby使用了50行代码,但Java至少要上千行代码)。
2、对象协作依赖于外部容器的组装,因此裸写代码是不可能的了,必须借助于外部的IoC容器。
对于Ruby来说,更加适合充血模型。因为ruby语言的表达能力非常强大,现在用ruby做企业应用的DSL是一个很热门的领域,DSL说白了就是用来描述某个行业业务逻辑的专用语言。
充血模型的好处是:
1、对象自洽程度很高,表达能力很强,因此非常适合于复杂的企业业务逻辑的实现,以及可复用程度比较高。
2、不必依赖外部容器的组装,所以RoR没有IoC的概念。
充血模型的坏处是:
1、对象高度自洽的结果是不利于大规模团队分工协作。一个编程个体至少要完成一个完整业务逻辑的功能。对于单个完整业务逻辑,无法再细分下去了。
2、随着业务逻辑的变动,领域模型可能会处于比较频繁的变动状态中,领域模型不够稳定也会带来web层代码频繁变动。
附件是完整的RoR版本的项目示例代码。要运行它,需要安装MySQL数据库(InnoDB表类型),Ruby和Ruby on rails环境。在MySQL数据库中分别创建demo数据库和demo_test数据库,修改demo\config\database.yml中的MySQL数据库配置,改成你的数据库密码。然后在项目跟目录下面执行:
rake db:migrate
rake db:test:clone_structure
rake test
即创建开发环境数据库,创建测试环境数据库,和执行所有的单元测试。领域模型代码位于demo\app\models目录下面;单元测试代码位于demo\test\units目录下面
- demo.zip (80.6 KB)
- 描述: 完整的RoR版本的项目示例代码
- 下载次数: 576
评论
25 楼
robbin
2007-03-03
complystill 写道
另外从Robbin的AR的代码中看不出级联删除的实现, TOB的模型中因为这个判断是应用重载 Tie.killingTargetNotify() 来实现的, 其中可以进行任意复杂的判断, 决定是删是留, 或者做其它处理. 这个地方如果不重载, 默认的处理在一个 Department 被删除后, 它所有的 Employee的 department 引用都会被清空. 删Employee时Task的处理类似.
不知道这个灵活性在AR中是怎么支持的.
不知道这个灵活性在AR中是怎么支持的.
class User < ActiveRecord::Base has_many :tasks, :dependent => :destroy end
24 楼
robbin
2007-03-03
见主贴内容
23 楼
nihongye
2007-03-03
要不把逻辑全部放实体对象里,要不就全部放servic里(除了那种一眼就觉得应该放实体的外)。不然感觉光琢磨这个该放那的问题,都会死掉好多脑细胞,而且还放不好。
22 楼
firebody
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);
//.....
}
}
* 关于这个主题的争论已经反反复复进行了好几次,每次的结论都不一样,不过这样的过程确实反映了大家的认识都在变化和提高。
从这个主题的讨论中,似乎印证了这样的道理:
不管是黑猫白猫,只要能够捉住老鼠的就是好猫。
在注重敏捷实践的团队中,更应该捉住这个定理,当然,更应该捉住这个道理的对立面:
不管什么东西,只要是阻碍效率的,都应该审视并修正它。
但是仍然具备它独特的优点,我谈谈我得几点看法:
* 它不能称为贫血模型。 它本身也可以建立 “和它自身状态紧密相连“的领域逻辑。
这里我加了一个前提:和它自身状态紧密相连
因为现有框架的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);
//.....
}
}
* 关于这个主题的争论已经反反复复进行了好几次,每次的结论都不一样,不过这样的过程确实反映了大家的认识都在变化和提高。
从这个主题的讨论中,似乎印证了这样的道理:
不管是黑猫白猫,只要能够捉住老鼠的就是好猫。
在注重敏捷实践的团队中,更应该捉住这个定理,当然,更应该捉住这个道理的对立面:
不管什么东西,只要是阻碍效率的,都应该审视并修正它。
21 楼
robbin
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类。
大部分的OO语言实现这样的模型都相差很小,因为其本身没有多少冗余,靠语法上的简洁性来来的简化微乎其微,相比之下,清晰和可阅读性更为重要一些。Java语言实现中只有getter/setter显得有些臃肿,不过不需要手工写,可以忽略不计。
·不必每个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。
20 楼
JavaInActoin
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类。
大部分的OO语言实现这样的模型都相差很小,因为其本身没有多少冗余,靠语法上的简洁性来来的简化微乎其微,相比之下,清晰和可阅读性更为重要一些。Java语言实现中只有getter/setter显得有些臃肿,不过不需要手工写,可以忽略不计。
·不必每个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显得有些臃肿,不过不需要手工写,可以忽略不计。
19 楼
歆渊
2007-03-03
嗯, 难以用 SQL 表达的复杂的统计匹配条件, AR就没法占到直接SQL的优势了, 可能写出来代码量也要增加了.
另外不知道这种情况下AR处理起并发访问来, 比如统计一个人的工作量中途时, 这个人有新增任务, 或者删除掉了一些任务, 结果会怎么样?
另关于TOB中处理复数的一点提示:
当相关对象只可能有一个时, 用一个 Kin<R,T> 类型的field来引用, 其中R是建立关联的持久关系类型, T是相关对象的类型.
当相关对象可能有多个时, 用一个 KinSet<R,T> 类型的field来引用, 其中R和T的含义相同. 同时KinSet<R,T> implements Iterable<Kin<R,T>>, 所以可以用在 for(Kin<R,T> kin : kinSet) 这样的循环中.
通过这种方式, 语义更加直白明确, 根本不需要再考虑 One-One, One-Many, Many-Many 的问题, 只要在自己这边看逻辑上相关对象是否可能有多个, 相应的定义一个field就可以了, 另一边也是独立的去看, 不用两边单复数情况先排列组合起来再分析了. 这点好像跟AR有点异曲同工.
另外不知道这种情况下AR处理起并发访问来, 比如统计一个人的工作量中途时, 这个人有新增任务, 或者删除掉了一些任务, 结果会怎么样?
另关于TOB中处理复数的一点提示:
当相关对象只可能有一个时, 用一个 Kin<R,T> 类型的field来引用, 其中R是建立关联的持久关系类型, T是相关对象的类型.
当相关对象可能有多个时, 用一个 KinSet<R,T> 类型的field来引用, 其中R和T的含义相同. 同时KinSet<R,T> implements Iterable<Kin<R,T>>, 所以可以用在 for(Kin<R,T> kin : kinSet) 这样的循环中.
通过这种方式, 语义更加直白明确, 根本不需要再考虑 One-One, One-Many, Many-Many 的问题, 只要在自己这边看逻辑上相关对象是否可能有多个, 相应的定义一个field就可以了, 另一边也是独立的去看, 不用两边单复数情况先排列组合起来再分析了. 这点好像跟AR有点异曲同工.
18 楼
clamp
2007-03-03
还是就例子本身来进行探讨吧,比较巧的是我恰巧实施过这样一个类似任务管理的项目,因此我觉得有必要把这个模型再复杂化一些.
在task(任务)上再加上kind(类别),start_time(开始时间),end_time(结束时间)
kind(类别)和user(员工)有多对多的关联关系,该关联关系可以认为是相对静态的.一个员工只能处理他相关类别的任务.
增加以下几个需求:
6.列出当前员工已经开始但尚未结束的任务
7.列出所有员工已经开始但尚未结束的任务
8.对某一类别,给所有和此一类别相关的员工,批量新增一批任务,这批任务的名称/开始时间/结束时间全部相同
9.对某一类别,统计当月总的任务数,已完成任务数,未完成任务数.对于任何一数值,均可以按员工进一步细化.
列出这些要求的目的是为了讨论我一直以来的一个想法:
OO和关系运算两者是适合不同的需求的,这两者互相都不能完全包容对方.领域建模是OO领域的概念.
OO更适用于对单个业务对象的操作
关系运算则适用于横跨多个业务对象的批量操作
robbin例子中的1/2/3是属于关系运算的范畴,只是因为相对简单,所以似乎可以用领域建模来包容,但是一旦复杂化之后难度就倍增了.
关键的问题在于:user类中的has_many:tasks和self.tasks
这是伪领域建模
ruby的一大创造就是广泛应用了复数概念,可以更自然的在OO框架下获得关系运算的好处,这一点我认为确实比java要强的多.
而在4/5这两个纯OO的需求之下,java仍然可以轻松满足(当然还有代码量的差别).
在task(任务)上再加上kind(类别),start_time(开始时间),end_time(结束时间)
kind(类别)和user(员工)有多对多的关联关系,该关联关系可以认为是相对静态的.一个员工只能处理他相关类别的任务.
增加以下几个需求:
6.列出当前员工已经开始但尚未结束的任务
7.列出所有员工已经开始但尚未结束的任务
8.对某一类别,给所有和此一类别相关的员工,批量新增一批任务,这批任务的名称/开始时间/结束时间全部相同
9.对某一类别,统计当月总的任务数,已完成任务数,未完成任务数.对于任何一数值,均可以按员工进一步细化.
列出这些要求的目的是为了讨论我一直以来的一个想法:
OO和关系运算两者是适合不同的需求的,这两者互相都不能完全包容对方.领域建模是OO领域的概念.
OO更适用于对单个业务对象的操作
关系运算则适用于横跨多个业务对象的批量操作
robbin例子中的1/2/3是属于关系运算的范畴,只是因为相对简单,所以似乎可以用领域建模来包容,但是一旦复杂化之后难度就倍增了.
关键的问题在于:user类中的has_many:tasks和self.tasks
这是伪领域建模
ruby的一大创造就是广泛应用了复数概念,可以更自然的在OO框架下获得关系运算的好处,这一点我认为确实比java要强的多.
而在4/5这两个纯OO的需求之下,java仍然可以轻松满足(当然还有代码量的差别).
17 楼
歆渊
2007-03-03
:S 那就搞不清楚这个UserList是指什么了... 是说数据库中所有User?
还是说就是可以单独管理的List对象? 如果是这样那可以单独定义为一个UserList持久类, 这样Dep也许还可以从它继承呢.
还是说就是可以单独管理的List对象? 如果是这样那可以单独定义为一个UserList持久类, 这样Dep也许还可以从它继承呢.
16 楼
庄表伟
2007-03-03
UserList!=Department
一个人进入公司,还没有进入部门,他就可以是一个员工了。
某某员工,加入某某部门或退出某某部门,甚至一个员工属于多个部门,都是自然的现象。
因此,Department可以维护一个“user-department”关系表。
但是,这不等于UserList
一个人进入公司,还没有进入部门,他就可以是一个员工了。
某某员工,加入某某部门或退出某某部门,甚至一个员工属于多个部门,都是自然的现象。
因此,Department可以维护一个“user-department”关系表。
但是,这不等于UserList
15 楼
歆渊
2007-03-03
庄表伟 写道
User与UserList是不一样的。
addUser这样的操作,就应该放在UserList里做。
把UserList要完成的工作打包在一起,命名为UserService,也是很自然的事情。因为UserList,很难被认为是一个对象实体。
addUser这样的操作,就应该放在UserList里做。
把UserList要完成的工作打包在一起,命名为UserService,也是很自然的事情。因为UserList,很难被认为是一个对象实体。
如果这个UserList是一个Department的所有成员列表, 那么这个List本身是不是可以认为是Department的一部分呢?
其实这是关系模型真正的好处, 你指定了一个 User 的 Dep 是xx, 那么那个Dep的成员列表在逻辑上就直接包含了这个User. TOB是把这种关系在系统级别自动实现了, addUser到一个Dep的memberList是直接依赖于这个User的dep属性的, 如果必须自己维护保障其一致性, 对持久应用来说应该是一种负担吧.
网络模型是从Dep用一个列表引用User, 而关系模型是设定User的Dep属性 (传统上称为Foreign Key, 而在TOB中发展成Tie引用).
14 楼
庄表伟
2007-03-03
User与UserList是不一样的。
addUser这样的操作,就应该放在UserList里做。
把UserList要完成的工作打包在一起,命名为UserService,也是很自然的事情。因为UserList,很难被认为是一个对象实体。
addUser这样的操作,就应该放在UserList里做。
把UserList要完成的工作打包在一起,命名为UserService,也是很自然的事情。因为UserList,很难被认为是一个对象实体。
13 楼
歆渊
2007-03-03
另外从Robbin的AR的代码中看不出级联删除的实现, TOB的模型中因为这个判断是应用重载 Tie.killingTargetNotify() 来实现的, 其中可以进行任意复杂的判断, 决定是删是留, 或者做其它处理. 这个地方如果不重载, 默认的处理在一个 Department 被删除后, 它所有的 Employee的 department 引用都会被清空. 删Employee时Task的处理类似.
不知道这个灵活性在AR中是怎么支持的.
不知道这个灵活性在AR中是怎么支持的.
12 楼
歆渊
2007-03-03
还有, employees 集合和 tasks 集合是数据库系统(TOB)维护的, 你birth一个employee或者task, 它就自动加到相应 department 对象 和 employee 对象的集合中去. 如果事务提交失败, 原来的集合内容就会自动恢复.
AR中还是需要应用自己去维护相关对象集合, 那么在事务失败的时候是否就需要应用自己修复这些集合呢?
AR中还是需要应用自己去维护相关对象集合, 那么在事务失败的时候是否就需要应用自己修复这些集合呢?
11 楼
歆渊
2007-03-03
另外注意Employee里的Password实现, 完全由这个类自己处理, 而不是提供 getter/setter 由 service 去处理.
10 楼
aardvark
2007-03-03
robbin 写道
我写这个例子不是想比较代码行数的。否则我就不会omit那么多信息了。我想说明的无非就是两点:
1、充血模型RoR实现起来很简单,而Java可以实现但有技术上的难度
2、RoR更加适合采用充血模型,而其复杂业务逻辑描述能力强于Java
只不过某些人硬是说Java也可以很方便的充血,而且RoR只能做简单web项目,硬说复杂企业业务逻辑Java描述能力更强。所以我在等这些人把代码给我贴出来,我好继续讨论下去。
Well,我也并没有在揣测你的意图。
对我来说,LOC makes sense。假如这个例子在Java中能用56行写完rich model(两倍于Ruby,够宽容了),我相信Java中没有理由不用rich model。
9 楼
歆渊
2007-03-03
我来贴一个基于TOB的充血模型吧.
完整可以编译运行代码在附件zip中.
(刚刚更新了一下, 原来的 javac6.bat 设置不对, 修改了一下)
要编译运行需要 JDK 6, AV6 (http://www.ableverse.com/download-free.jsp), 还有一个底层数据库. 推荐用 H2 (http://h2database.com), 缺省也是这个, 否则需要修改 meta/tob.test.meta 里, 换用别的数据库.
可以修改 javac6.bat, 设置 JAVA_HOME 为一个 JDK 6的目录, 同时设置解压的AV6路径, 然后双击就可以编译通过了, 运行我想大家都会, 注意把 meta 也加到 classpath 里去.
先贴测试类:
部门类: 注意其中的 calcTotalSalary() 和 calcTotalWorkHours() 实现
雇员类: 注意其中 calcTotalWorkHours() 的实现. 这里假定一个部门被删除是它的雇员也全部删除.
任务类: 这里假定做一个任务的雇员被删除时任务自动跟着删除
其中一次的运行结果:
完整可以编译运行代码在附件zip中.
(刚刚更新了一下, 原来的 javac6.bat 设置不对, 修改了一下)
要编译运行需要 JDK 6, AV6 (http://www.ableverse.com/download-free.jsp), 还有一个底层数据库. 推荐用 H2 (http://h2database.com), 缺省也是这个, 否则需要修改 meta/tob.test.meta 里, 换用别的数据库.
可以修改 javac6.bat, 设置 JAVA_HOME 为一个 JDK 6的目录, 同时设置解压的AV6路径, 然后双击就可以编译通过了, 运行我想大家都会, 注意把 meta 也加到 classpath 里去.
先贴测试类:
public class TestTaskTrack { public static void main(String[] args) throws Exception { final int rand = new Random(System.currentTimeMillis()).nextInt(8000); final TheObjectBase<SQLQuerier> tob = // TheObjectBase.create(SQLQuerier.class); TheObjectBase.create(MetaBundle.get("tob.test")); if (rand % 3 == 0 || tob.querier.count(Department.class, null) < 1) { tob.startNewTransaction(); Department dep = tob.birth(new Department("Test Dep" + rand)); System.out.println("New Department with ID " + dep.getID() + " created."); tob.commitCurrentTransaction(); } if (rand % 2 == 0 || tob.querier.count(Employee.class, null) < 1) { tob.startNewTransaction(); tob.querier.query(new ObjectProcessor<Department>() { public boolean processTheObject(long ordinal, Department o) { try { Employee e = tob.birth(new Employee(o, "Tester" + rand, "Male", rand)); System.out.println("New Employee with ID " + e.getID() + " created."); } catch (FetalDeathException e) { e.printStackTrace(); } return false; } }, 1L, Department.class, "1=1 ORDER BY timeMod DESC"); tob.commitCurrentTransaction(); } tob.querier.query(new ObjectProcessor<Employee>() { public boolean processTheObject(long ordinal, Employee o) { try { Task t = tob.birth(new Task(o, "Do Some Test " + rand, rand / 10)); System.out.println("New Task with ID " + t.getID() + " created."); } catch (FetalDeathException e) { e.printStackTrace(); } return false; } }, 1L, Employee.class, "1=1 ORDER BY timeMod DESC"); tob.commitAllTransactions(); tob.querier.query(new ObjectProcessor<Department>() { public boolean processTheObject(long ordinal, Department o) { System.out.println("Department: " + o.getName()); System.out.println(" Num Of Employees: " + o.getEmployees().size()); System.out.println(" Total Salary: " + o.calcTotalSalary()); System.out.println(" Total Works: " + o.calcTotalWorkHours() + " hour(s)."); System.out.println(" With Employees:"); for (Kin<?, Employee> e : o.getEmployees()) { System.out.println(" Employee: " + e.getO().getName()); System.out.println(" Salary: " + e.getO().getSalary()); System.out.println(" Num Of Tasks: " + e.getO().getTasks().size()); System.out.println(" Total Works: " + e.getO().calcTotalWorkHours() + " hour(s)"); System.out.println(" With Tasks: " + e.getO().getName()); for (Kin<?, Task> t : e.getO().getTasks()) { System.out.println(" Task: " + t.getO().getName()); System.out.println(" Need: " + t.getO().getWorkHours() + " hour(s) work"); } } return true; } }, -1L, Department.class, DepartmentQ.NAME + " LIKE 'Test%'"); tob.shutdown(); } }
部门类: 注意其中的 calcTotalSalary() 和 calcTotalWorkHours() 实现
@Swappable public class Department extends TheObject { private String name; private KinSet<Employee, Employee> employees = null; public Department(String name) { this.name = name; } protected Department() { } public int calcTotalSalary() { int total = 0; for (Kin<?, Employee> e : employees) total += e.getO().getSalary(); return total; } public int calcTotalWorkHours() { int total = 0; for (Kin<?, Employee> e : employees) total += e.getO().calcTotalWorkHours(); return total; } public String getName() { return name; } @Writing public void setName(String name) { this.name = name; } public KinSet<Employee, Employee> getEmployees() { return employees; } }
雇员类: 注意其中 calcTotalWorkHours() 的实现. 这里假定一个部门被删除是它的雇员也全部删除.
@Swappable public class Employee extends TheRelation { private String password; private String name; private String gender; private int salary; protected Tie<Department> department; private KinSet<Task, Task> tasks = null; public Employee(Department dep, String name, String gender, int salary) { this.department = new Tie<Department>(dep) { @Override protected void killingTargetNotify() throws ObjectDependedException { // whole department laid off? Employee.this.die(); } }; this.name = name; this.gender = gender; this.salary = salary; } protected Employee() { } private static String encryptPassword(char[] passwd) { String encrypted = "encrypted password"; // implement with digest Arrays.fill(passwd, '\0'); return encrypted; } public boolean matchPassword(char[] passwd) { return encryptPassword(passwd).equals(this.password); } @Writing public void setPassword(char[] password) { this.password = encryptPassword(password); } public int calcTotalWorkHours() { int total = 0; for (Kin<?, Task> t : tasks) total += t.getO().getWorkHours(); return total; } public String getGender() { return gender; } @Writing public void setGender(String gender) { this.gender = gender; } public String getName() { return name; } @Writing public void setName(String name) { this.name = name; } public int getSalary() { return salary; } @Writing public void setSalary(int salary) { this.salary = salary; } public Department getDepartment() { return department.o; } public KinSet<Task, Task> getTasks() { return tasks; } }
任务类: 这里假定做一个任务的雇员被删除时任务自动跟着删除
@Swappable public class Task extends TheRelation { protected Tie<Employee> owner; private String name; private int workHours; public Task(Employee owner, String name, int workHours) { this.owner = new Tie<Employee>(owner) { @Override protected void killingTargetNotify() throws ObjectDependedException { // no one works on this task again? Task.this.die(); } }; this.name = name; this.workHours = workHours; } protected Task() { } public String getName() { return name; } @Writing public void setName(String name) { this.name = name; } public int getWorkHours() { return workHours; } @Writing public void setWorkHours(int workHours) { this.workHours = workHours; } public Employee getOwner() { return owner.o; } }
其中一次的运行结果:
New Employee with ID 7 created. New Task with ID 8 created. Department: Test Dep1562 Num Of Employees: 3 Total Salary: 14258 Total Works: 1995 hour(s). With Employees: Employee: Tester5450 Salary: 5450 Num Of Tasks: 1 Total Works: 545 hour(s) With Tasks: Tester5450 Task: Do Some Test 5450 Need: 545 hour(s) work Employee: Tester1562 Salary: 1562 Num Of Tasks: 1 Total Works: 156 hour(s) With Tasks: Tester1562 Task: Do Some Test 1562 Need: 156 hour(s) work Employee: Tester7246 Salary: 7246 Num Of Tasks: 2 Total Works: 1294 hour(s) With Tasks: Tester7246 Task: Do Some Test 7246 Need: 724 hour(s) work Task: Do Some Test 5707 Need: 570 hour(s) work
8 楼
robbin
2007-03-03
aardvark 写道
complystill 写道
嗯, 我觉得其中有个情况是, 搞软件开发的都多少有些倾向, 希望自己做的东西能有点噱头, 跟人提起来能涨脸的, 至少是不因为太直白而被人鉴定为没什么技术含量. 因为大家心里实质性的存在这么一个等式:
项目技术含量 ~=~ 其开发者身价
Java的特点就是太直白, 直接在类上做也做不出什么花样来, 所以要奔着分布式, Service等等方向突破.
而Ruby很灵活, 光是一个类就有很多东西好玩儿, 不搞Service也能出彩.
所以个人感觉: Java不是不能做Rich Domain Model, 而是大牛们觉得这么做了会掉价. 看看 POJO 的来历:
http://www.martinfowler.com/bliki/POJO.html
其实就是一个 Fancy Name 的问题.
想做Java Rich Domain Model的同仁们可以试试TOB, 所有逻辑都可以放到持久类里去, 也不会有 "持久实现代码" 过来混局, 更不用实现什么 DAO. 最好的起点是看看 http://wow.dev.java.net 的开源代码.
项目技术含量 ~=~ 其开发者身价
Java的特点就是太直白, 直接在类上做也做不出什么花样来, 所以要奔着分布式, Service等等方向突破.
而Ruby很灵活, 光是一个类就有很多东西好玩儿, 不搞Service也能出彩.
所以个人感觉: Java不是不能做Rich Domain Model, 而是大牛们觉得这么做了会掉价. 看看 POJO 的来历:
http://www.martinfowler.com/bliki/POJO.html
其实就是一个 Fancy Name 的问题.
想做Java Rich Domain Model的同仁们可以试试TOB, 所有逻辑都可以放到持久类里去, 也不会有 "持久实现代码" 过来混局, 更不用实现什么 DAO. 最好的起点是看看 http://wow.dev.java.net 的开源代码.
正所谓“仁者见仁,智者见智”,在你看来别人心里也是有你那么个等式。Ruby和Java就是技术上的区别,别人谈的是技术上的区别,我看到的也是技术上的区别,到你这就不是技术上的区别了。
就Robbin的例子,你做个Java的Rich Domain Model要多少行程序?Rails用多少行程序?
我写这个例子不是想比较代码行数的。否则我就不会omit那么多信息了。我想说明的无非就是两点:
1、充血模型RoR实现起来很简单,而Java可以实现但有技术上的难度
2、RoR更加适合采用充血模型,而其复杂业务逻辑描述能力强于Java
只不过某些人硬是说Java也可以很方便的充血,而且RoR只能做简单web项目,硬说复杂企业业务逻辑Java描述能力更强。所以我在等这些人把代码给我贴出来,我好继续讨论下去。
7 楼
robbin
2007-03-03
引用
对象的状态和行为一定要封装在一起吗,这些个概念都要看抽象的程度的吧?同样的一个entity,在不同的service中会有不同的方法,domain model难道就是把所有的东西都放到一起来?这么说吧,一个人这么个entity,可以做的事情很多吧,那好全部放一起...。
Martin Fowler在PoEAA中认为,越是企业应用的复杂业务逻辑,Rich domain model架构越合适,对于简单企业应用,贫血模型也不错,但是他认为随着业务逻辑复杂度上升,贫血模型的弊端会被放大,变得开始不合适了。
我的立论是:
其实即便在上一个讨论贴中,我都从来没有说过贫血模型就不好,充血模型就好这样的话。我只说过Java要实现充血模型有技术上的难度,而RoR原生支持充血模型,由于充血模型更适合于企业复杂的业务逻辑,因此RoR理论上要比Java描述复杂企业业务逻辑的能力更强,更加适合业务逻辑复杂的企业应用项目的开发。
但请注意我从来没有说贫血模型就不好,充血模型就一定好的话。其实模型无所谓好坏,各自有不同的合适的应用场景罢了。所以你非要和我争,说对象状态和行为分离也没有什么不好,贫血也没有什么不好,Martin Fowler说的也不一定就是对的,那靶子就歪了。是的,我完全同意你的观点,但我的帖子讲的不是这个话题,我的帖子不是探讨哪个模型好哪个模型不好的,我的帖子是探讨Java和RoR哪个比较适合充血模型,哪个比较合适复杂业务逻辑的描述的。
引用
UserService本身就是针对user的一个抽象而已,不考虑我们现在的spring等的技术,也不考虑dao的注入,用个jdbc,把user的功能和userservice 里面的方法放一起实现也不是不可以?
你可以不用spring/hiberaten,可以只用JDBC,不妨把你只用JDBC的代码贴出来,我等这个已经等了很久了。n个人回贴说如果不要XX,不要XX,Java也可以充血模型,但是还没有一个人把代码给我贴出来证明给我看。
6 楼
aardvark
2007-03-03
complystill 写道
嗯, 我觉得其中有个情况是, 搞软件开发的都多少有些倾向, 希望自己做的东西能有点噱头, 跟人提起来能涨脸的, 至少是不因为太直白而被人鉴定为没什么技术含量. 因为大家心里实质性的存在这么一个等式:
项目技术含量 ~=~ 其开发者身价
Java的特点就是太直白, 直接在类上做也做不出什么花样来, 所以要奔着分布式, Service等等方向突破.
而Ruby很灵活, 光是一个类就有很多东西好玩儿, 不搞Service也能出彩.
所以个人感觉: Java不是不能做Rich Domain Model, 而是大牛们觉得这么做了会掉价. 看看 POJO 的来历:
http://www.martinfowler.com/bliki/POJO.html
其实就是一个 Fancy Name 的问题.
想做Java Rich Domain Model的同仁们可以试试TOB, 所有逻辑都可以放到持久类里去, 也不会有 "持久实现代码" 过来混局, 更不用实现什么 DAO. 最好的起点是看看 http://wow.dev.java.net 的开源代码.
项目技术含量 ~=~ 其开发者身价
Java的特点就是太直白, 直接在类上做也做不出什么花样来, 所以要奔着分布式, Service等等方向突破.
而Ruby很灵活, 光是一个类就有很多东西好玩儿, 不搞Service也能出彩.
所以个人感觉: Java不是不能做Rich Domain Model, 而是大牛们觉得这么做了会掉价. 看看 POJO 的来历:
http://www.martinfowler.com/bliki/POJO.html
其实就是一个 Fancy Name 的问题.
想做Java Rich Domain Model的同仁们可以试试TOB, 所有逻辑都可以放到持久类里去, 也不会有 "持久实现代码" 过来混局, 更不用实现什么 DAO. 最好的起点是看看 http://wow.dev.java.net 的开源代码.
正所谓“仁者见仁,智者见智”,在你看来别人心里也是有你那么个等式。Ruby和Java就是技术上的区别,别人谈的是技术上的区别,我看到的也是技术上的区别,到你这就不是技术上的区别了。
就Robbin的例子,你做个Java的Rich Domain Model要多少行程序?Rails用多少行程序?
发表评论
-
WebObjects的来龙去脉
2012-06-08 15:30 7716在知乎上回答的一个问题:http://www.zhihu.co ... -
缓存技术浅谈
2010-09-24 18:08 21885有我在两年前写的一个培训的ppt,是介绍缓存知识的。有兴趣的可 ... -
对领域模型实现的总结性观点
2008-11-30 15:16 19620陶文发起的对领域模型 ... -
发现JBoss Seam很棒呀!有用Seam做过项目的吗?
2008-07-06 20:56 30617上周去见了一个朋友Mark,他应邀在Red Hat的研讨会上面 ... -
Spring Application Platform - SpringSource的应用服务器发布
2008-05-05 17:04 69182008年的5.1劳动节,Spring ... -
Warp framework - 一个相当有前途的Java轻量级Web开发框架
2008-03-06 15:24 22692Warp framework 是最近刚刚 ... -
Google Android会成为手机领域的微软Windows吗?
2007-11-16 17:23 9677Google gPhone手机的传言已经沸沸扬扬好几个月了,然 ... -
Java已经过时了吗?
2007-07-02 15:43 59788在四年以前,当我开始 ... -
Java开源框架发展的遐想
2007-05-23 00:04 34862上周末在杭州网侠大会做演讲的时候,我说:Java开源框架的革命 ... -
漫谈应用缓存的命中率问题
2007-05-09 14:19 26596这篇文章源自于: http://www.iteye.com/ ... -
为什么ORM性能比iBATIS好?
2007-05-06 11:16 34610缓存是有很多层次的,有web server前端缓存,有动态页面 ... -
点评Grails vs RoR
2007-03-30 17:49 8310Grails的革新和RoR相比,非常不彻底,很多地方兼容Jav ... -
缓存简述
2007-03-30 09:55 12293缓存实现的层面有很多: 1、对象缓存 由ORM框架提供,透明 ... -
JRuby0.9.8,正式宣布支持ruby on rails
2007-03-07 10:35 15711http://jruby.codehaus.org/ 自从S ... -
可以开始用Struts2.0了
2007-02-27 14:56 56159http://struts.apache.org/ Apac ... -
Google Guice - 比Spring快100倍的IoC容器
2007-02-27 14:46 58311http://code.google.com/p/google ... -
Spring2.0和EJB3.0随谈
2007-02-08 14:26 18485Spring自从2003年发布以来 ... -
Java程序员的推荐阅读书籍
2007-02-07 20:12 101459《Java程序员的推荐阅读 ... -
应该如何正确使用Quartz
2006-12-27 11:40 34296对于Web容器来说,最忌讳应用程序私自启动线程,自行进行线程调 ... -
静态类型语言的优势究竟是什么?
2006-11-13 10:03 33617在参与这个讨论的过程中,产生了一个新的话题,很想和大家探讨一下 ...
相关推荐
通常,领域模型包含了业务实体(Entities)、值对象(Value Objects)、聚合(Aggregates)、领域事件(Domain Events)、服务(Services)以及领域驱动设计(DDD)中的其他元素。 1. **业务实体(Entities)**:...
"domainmodel-0.12.tar.gz" 是一个针对Python的特定库,名为"domainmodel",版本号为0.12。这个库很可能包含了特定领域的数据模型和业务逻辑,方便开发者进行结构化编程和领域驱动设计。 "domainmodel"库可能包含...
python库。 资源全名:domainmodel-0.2-py2.4.egg
领域模型 DomainModel 领域模型是软件设计中的一种重要概念,它旨在理解系统如何工作,包括内部行为和外部行为。领域模型的目的是为了确定系统中各个元素之间的交互关系,以便产生外部行为。 领域模型为什么重要?...
在"XText Domainmodel示例(按发行版)"这个项目中,我们可以推测这是一系列按照不同版本组织的示例,展示了如何使用XText来创建和管理领域模型。XText通过其强大的语法定义能力,允许开发者定义自己的领域模型语言...
**领域模型(Domain Model)——理论、现实与梦想** 领域模型是软件开发中的核心概念,尤其是在面向对象设计和企业级应用开发中。它是一个反映业务领域的抽象模型,包含了业务规则、实体、值对象、聚合、服务等关键...
MapperRegistry是工厂方法的变形?摘要本文通过由ActiveRecord模式到DataMapper模式(使用工厂方法)再到DataMapper模式(使用MapperRegistry)的一系列重构,探讨模式背后隐藏的思想和面向对象设计原则。...
"fast"和"rich domain model"可能是在强调Java MVC框架在处理复杂业务逻辑时的高效性能和强大的领域模型支持。领域模型是业务对象在软件中的表示,它封装了业务规则和数据。一个丰富的领域模型可以帮助开发者更好地...
jar包,官方版本,自测可用
/* 在这里启动 device-model */ 创建块设备界面(); /* Create the block device interface (blkif) for the vm. The vm needs a blkif even if it doesn't have any disks at creation time */ } ``` 3. 主函数: ...
propose a regularized unsupervised optimal transportation model to perform the alignment of the representations in the source and target domains. We learn a transportation plan matching both PDFs, ...
最后,我们将对Domain Generalization的未来研究方向进行讨论和展望。 Domain Generalization的应用前景非常广阔。例如,在计算机视觉中,Domain Generalization可以应用于物体识别、图像分割、动作识别、人脸识别...
领域模型(Domain Model)是软件开发中的一个重要概念,特别是在领域驱动设计(Domain-Driven Design, DDD)中占据核心地位。Domain Object,即领域对象,是领域模型中的基本元素,它们代表了业务领域的核心概念和...
首先,我们来理解DDD的核心概念——领域模型(Domain Model)。领域模型是业务逻辑的抽象表示,它包含了业务领域的实体(Entities)、值对象(Value Objects)、领域服务(Domain Services)和仓储(Repositories)...
The main focus of the book is how a Domain Model could be constructed to be clean, yet still be persistence-friendly. It shows what the persistence solution could look like for such a Domain Model and...
Continuous time modeling: time domain, Laplace domain, and FOPDT models Feedforward, cascade, override, and single-variable inferential control approaches One and two degree of freedom Internal ...