锁定老帖子 主题:domain model的延伸讨论
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-03-03
引用 一个简单的公司工时管理系统,记录员工的个人信息,每个员工的工作任务分配,以及工作所属类别(例如开发,还是测试,还是培训等等),其中每个员工有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目录下面 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-03-03
引用 这个Java版本的实现其实还是贫血的模型,按照面向对象的基本原则,对象的状态应该和它的行为封装在一起,因此User和UserService实际上违反了面向对象的原则了,因此是贫血的领域模型。按照构建良好的领域模型设计原则,UserService应该被去掉,其方法必须放在User对象中,同理TaskService也应该被去掉,方法要放入Task类。 对象的状态和行为一定要封装在一起吗,这些个概念都要看抽象的程度的吧?同样的一个entity,在不同的service中会有不同的方法,domain model难道就是把所有的东西都放到一起来?这么说吧,一个人这么个entity,可以做的事情很多吧,那好全部放一起...。 就好比前段时间“OO的丧钟”,以及对“一切皆对象”的质疑这些帖子一样,是不是在个人主观上抛弃了对抽象的范围、抽象的横纵等的考虑呢? UserService本身就是针对user的一个抽象而已,不考虑我们现在的spring等的技术,也不考虑dao的注入,用个jdbc,把user的功能和userservice 里面的方法放一起实现也不是不可以? ror能简单的实现这样的东西,java又不是实现不了,说ror简单,有时候这个比较是不是类似百货超市和一般的路边小店的比较呢。 拿ror的简单快捷来和java技术相比较,个人觉得不具有丝毫可比性,当然sun各个方面的软弱无能也是造成java开发人员工作累的一个很重要的原因 ,如果sun能拿出象样的东西也许大家就不需要试试ibats、hibernate 也不需要试试dom4j、jdom等等,但是个人的疲倦感又是何其的渺小呢。 而且现在java的企业软件理论上又可供java桌面程序来使用,ror的确是web开发的利器,但和java比层次明显又不一样了。 ps:初生牛犊不怕虎,鼓起勇气跟个贴,一直做web,也说是企业应用,不过感觉基本也就crud的累加了,经验有限,论坛经常能看到的经典书籍也基本没看过,草根程序员吧 。 |
|
返回顶楼 | |
发表时间:2007-03-03
java的这个模式是为了将来系统升级扩大的时候用的, 改动没那么多, 多加几个SERVICE METHODS就是了. 基础比较扎实.
RUBY语言简洁可读性强, 也不失是个好的选择, 看具体的项目情况决定啊. 无所谓谁好谁坏, 最合适的就是最好的. |
|
返回顶楼 | |
发表时间:2007-03-03
如果不要DAO,不要SERVICE,还是都一样。
我在考虑一个问题,在目前,我们是否需要在编码一级实现OO设计? 我认为以目前的条件,还不足以这样设计, 应当定义一层逻辑的OO层,也就是用配置来描述,这个描述是遵照OO的标准去实现,底层实现不必完全遵照OO的标准。 也就是说,User对象可以不必设置Task集合属性,但在逻辑层的配置描述中会设置Tasks的属性。这样可以解决很多问题。 |
|
返回顶楼 | |
发表时间:2007-03-03
嗯, 我觉得其中有个情况是, 搞软件开发的都多少有些倾向, 希望自己做的东西能有点噱头, 跟人提起来能涨脸的, 至少是不因为太直白而被人鉴定为没什么技术含量. 因为大家心里实质性的存在这么一个等式:
项目技术含量 ~=~ 其开发者身价 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 的开源代码. |
|
返回顶楼 | |
发表时间:2007-03-03
smilelee74 写道 如果不要DAO,不要SERVICE,还是都一样。
我在考虑一个问题,在目前,我们是否需要在编码一级实现OO设计? 我认为以目前的条件,还不足以这样设计, 应当定义一层逻辑的OO层,也就是用配置来描述,这个描述是遵照OO的标准去实现,底层实现不必完全遵照OO的标准。 也就是说,User对象可以不必设置Task集合属性,但在逻辑层的配置描述中会设置Tasks的属性。这样可以解决很多问题。 可以省略DAO,也可以省略Service,请你把符合rich domain object的Java代码贴出来,别光说不练。 |
|
返回顶楼 | |
发表时间: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 的开源代码. 正所谓“仁者见仁,智者见智”,在你看来别人心里也是有你那么个等式。Ruby和Java就是技术上的区别,别人谈的是技术上的区别,我看到的也是技术上的区别,到你这就不是技术上的区别了。 就Robbin的例子,你做个Java的Rich Domain Model要多少行程序?Rails用多少行程序? |
|
返回顶楼 | |
发表时间: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也可以充血模型,但是还没有一个人把代码给我贴出来证明给我看。 |
|
返回顶楼 | |
发表时间: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 的开源代码. 正所谓“仁者见仁,智者见智”,在你看来别人心里也是有你那么个等式。Ruby和Java就是技术上的区别,别人谈的是技术上的区别,我看到的也是技术上的区别,到你这就不是技术上的区别了。 就Robbin的例子,你做个Java的Rich Domain Model要多少行程序?Rails用多少行程序? 我写这个例子不是想比较代码行数的。否则我就不会omit那么多信息了。我想说明的无非就是两点: 1、充血模型RoR实现起来很简单,而Java可以实现但有技术上的难度 2、RoR更加适合采用充血模型,而其复杂业务逻辑描述能力强于Java 只不过某些人硬是说Java也可以很方便的充血,而且RoR只能做简单web项目,硬说复杂企业业务逻辑Java描述能力更强。所以我在等这些人把代码给我贴出来,我好继续讨论下去。 |
|
返回顶楼 | |
发表时间: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 里去. 先贴测试类: 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 |
|
返回顶楼 | |