锁定老帖子 主题:贫血的Domain Model
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-05-10
public class Employee { private long id; private int salary; private String name; private Department department; private RichSet<Task> tasks = new DefaultRichSet<Task>(); public Employee(String name, int salary, Department department) { this.name = name; this.salary = salary; this.department = department; } protected Employee() { } public long getId() { return id; } public int getSalary() { return salary; } public Department getDepartment() { return department; } public Task assign(String taskName) { Task task = new Task(taskName, this, new Date()); tasks.add(task); return task; } public RichSet<Task> getProcessingTasks() { return tasks.find("startTime").le(new Date()).find("endTime").isNull(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; if (name != null ? !name.equals(employee.name) : employee.name != null) return false; return true; } public int hashCode() { return 31 * (name != null ? name.hashCode() : 0); } } |
|
返回顶楼 | |
发表时间:2008-05-10
ronghao 写道 在我实际的编程里,Domain对象里大量注入了DAO,我并没有觉得有什么不好,好处在于带来了一致的编程体验,唯一的问题在于给测试带来了麻烦,单元测试需要写很多stub。在特定于工作流引擎的情况下,很多同事主张用事件的方式来解除Domain对象与DAO的耦合,但是我觉得没有必要。
楼主的解决方案我觉得还是存在一定局限性的。比如如果Domain对象的业务方法需要执行业务逻辑并插入或更新数据时。 同意,注入DAO没什么不好。测试的时候起一个内存数据库就好了。再弄几个db fixture,状态准备也一样轻松搞定。 执行业务逻辑就用代码写好了。更新数据就是修改自己的状态。service会去最后把domain对象save回去的,因为hibernate可以遍历对象图找出改动,所以这个我们不用操心。 插入数据可以通过添加元素到一个特殊的RichList来实现。 Department department = new Department("R&D"); department.hire("taowen", 100); repository.of(Department.class).add(department); public interface Repository { <T> RichList<T> of(Class<T> entityClass); } 简单来说,就是把一张表抽象为一个RichList。添加记录就是add,删除记录就是remove。这个RichList在domain中使用,但是domain并不用担心这个list是怎么构建出来的。在单元测试的时候可以使用一个InMemory的List,在产品代码中这个List是一个包装着Hibernate Session的家伙。 |
|
返回顶楼 | |
发表时间:2008-05-10
Martin Fowler的回复
引用 Sounds rather like Linq. Certainly adding this kind of query logic is part of getting people away from Anemia, so it's a useful option. The Query Object in P of EAA is roughly similar kind of idea - build queries in domain terms and then translate them to either/or in memory navigation or SQL. |
|
返回顶楼 | |
发表时间:2008-05-10
got it, thanks
|
|
返回顶楼 | |
发表时间:2008-05-10
taowen 写道 同意,注入DAO没什么不好。测试的时候起一个内存数据库就好了。再弄几个db fixture,状态准备也一样轻松搞定。 执行业务逻辑就用代码写好了。更新数据就是修改自己的状态。service会去最后把domain对象save回去的,因为hibernate可以遍历对象图找出改动,所以这个我们不用操心。 插入数据可以通过添加元素到一个特殊的RichList来实现。 Department department = new Department("R&D"); department.hire("taowen", 100); repository.of(Department.class).add(department); public interface Repository { <T> RichList<T> of(Class<T> entityClass); } 简单来说,就是把一张表抽象为一个RichList。添加记录就是add,删除记录就是remove。这个RichList在domain中使用,但是domain并不用担心这个list是怎么构建出来的。在单元测试的时候可以使用一个InMemory的List,在产品代码中这个List是一个包装着Hibernate Session的家伙。 nice! 可是我怎么总感觉RichList像个变了性的通用DAO呢,也就是用它将domain与数据持久化代码进行了一次隔离。呵呵 |
|
返回顶楼 | |
发表时间:2008-05-10
对啊,就是在Domain <-> Hibernate之间建立了一个隔离层。只不过它本身是一个collection而已。其实自身是一个generic dao。
|
|
返回顶楼 | |
发表时间:2008-05-10
另一个方面的问题:一个主业务Domain Object,其中可能包含20来个字段,可能需要和多个其他的Domain Object关联。这个时候,按照Rich Domain Object的理论和你的实现,可以把这些查询逻辑或者持久化逻辑全部封装在Domain Object的内部。那么这个Domain Object会有多少行?在团队合作中,如何保持它内部语义的不重复性?
|
|
返回顶楼 | |
发表时间:2008-05-10
按照马丁的意思,Rich Domain Object无非是想程序更好维护,那我想,既然这样想了,那就直奔主题,不要被隔离的思想束缚住,Domain Object就直接引用hibernate session或者dao甚至jdbc connection,就好了.看看达到这个更好维护的目的没有。老是想着隔离,隐藏,呢,创建出一些新的隔离对象,干扰了视线。
|
|
返回顶楼 | |
发表时间:2008-05-10
查询逻辑,比如说对一个collection的过滤是应该封装在domain中的。持久化逻辑并不是什么逻辑。持久化只是一种domain的存在形式。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。 |
|
返回顶楼 | |
发表时间:2008-05-10
nihongye 写道 按照马丁的意思,Rich Domain Object无非是想程序更好维护,那我想,既然这样想了,那就直奔主题,不要被隔离的思想束缚住,Domain Object就直接引用hibernate session或者dao甚至jdbc connection,就好了.看看达到这个更好维护的目的没有。老是想着隔离,隐藏,呢,创建出一些新的隔离对象,干扰了视线。
同意。其实domain对象和数据库绑定,直接用session没啥问题。ActiveRecord不也这么干的嘛。人家连字段都是数据库的。 |
|
返回顶楼 | |