- 浏览: 193503 次
- 性别:
- 来自: 北京
最新评论
-
bzhao:
开启了一个很牛逼的话题!
提高程序员的准入门槛? -
迷人阳光love:
不知两年多了,lz整理的从问题出发的模式是否还在??很是渴望得 ...
学习模式,不如先了解问题 -
迷人阳光love:
lz说到了我的心坎里,这是您10年发的文章,现在 也没找到一个 ...
学习模式,不如先了解问题 -
toafu:
我的理解是,持续集成和交付也解决不了人的问题。
为什么我的敏捷项目有如此多的问题? -
liaofeng_xiao:
《不可承受的生命之轻》 ,真没看出太多内容~
近期看的书和电影
好老的话题啦。拿出来炒炒冷饭。各位见谅。
——————————————————————
Domain Model贫血是说属于Domain Model的逻辑没有放在Domain Model中。那是哪些逻辑没有放到Domain Model中,从而导致贫血一说呢?原因有很多,但是我认为最主要是Service中的那些逻辑。而这些逻辑又有一个共同的特点就是依赖于DAO,或者说需要查询数据库。Robbin的帖子:http://www.iteye.com/topic/57075,举了一个很好的例子。我取其中的一个部分在这里做演示用。
这是一个很简单的一对多的关系。现在要查找指定员工的处理中的任务。如果忽略数据库的存在,我想大部分的同志都会这么实现:
这也符合OO数据隐藏的基本原则。但是如果有数据库存在,怎么写就不那么容易决定了。如果没有Hibernate这样的ORM。那肯定是:
那我觉得,这就导致了Domain Model的失血。因为没有数据库的时候,这这个方法本来应该在Employee上的,而不是在DAO上的。
如果有Hibernate呢?是不是我就可以把这段代码写到Employee里面去呢?
还是有问题。因为访问tasks的时候,Hibernate会去加载数据。getProcessingTask会便利所有的task。如果task的数量很多,这降极大的影响性能。所以为了能够享受到关系数据库查询速度的好处,我们要还要利用SQL。于是DAO又再次地找到了自己的位置。那么怎么解决这个问题呢?在http://www.iteye.com/topic/57075的回帖中nihongye同学提出了一个解决方案。本质来说就是不让hibernate来映射tasks,改由查询来获得。加上Spring支持的@Configurable标记,我们可以把代码写成这样
我们当然还可以把TaskDao替换成变的形式。比如http://www.iteye.com/topic/65406里firebody提到的那样。但是本质上来说,都是让Employee能够直接去使用Hibernate做查询。但是坏处是给Domain纯净分子的口实。虽然,我认为和ActiveRecord类似,entity绑定在数据库上没啥不好。另外一个缺点就是,要么仍然有一个Dao来封装查询逻辑的实现,要么Employee的实现中出现太多的hibernate api,而且写法复杂。这也就是Robbin一再强调,ActiveRecord那样的api在Java世界中不是不可以,而是实现复杂难度高的原因。注入可以解决问题,但是对Hibernate的依赖强而且写法丑陋。
那么有没有更优美的方案呢?有:
RichSet是我自己编造的一个名字。它是一个”rich“的set。其实就是附加了一些find,sort,sum之类的操作。
DefaultRichSet是这些附加操作的内存版本的实现。这个能解决问题么?还是不能,这时候getProcessingTasks的时候,richSet还是去遍历内部的_tasks,然后把结果过滤出来。而且,hibernate还拒绝接受这样set。为了让hibernate能够接受RichSet,我们需要这么写配置文件。
通过指定RichEntityTuplizer,我们可以控制Hibernate的动态增强过程。
这样,tasks就不再是DefaultRichSet了。Hibernate会尝试去增强为PersisentSet,但是被RichEntityTuplizer改写为增强HibernateRichSet了。这样就形成了HibernateRichSet -> PersisentSet -> DefaultRichSet -> HashSet 的包含关系。
当用户尝试在tasks上做find的时候,就不再是DefaultRichSet来做collection遍历了,而是HibernateRichSet去拼装一个DetachedCriteria。最后当用户在查询的结果上取size()或者取具体元素的时候,这个criteria被拿去求值。
通过使用RichSet,domain model具有了对自身进行查询的能力。更重要的是,这种能力的获得,不是通过把Hibernate session注入到domain model中。domain仍然是纯净的,没有依赖于数据库的东西。而且domain是可以脱离容器使用的。new Employee出来就可以直接使用,测试。区别只是经过repository增强的entity会使用sql,而transient的entity所有的查询都是通过遍历实现的。
没有了DAO之后,Domain Model是不是能够摆脱贫血的困扰呢?这个还需要观察。不过我认为至少是向前迈了一步了。
确实就实际设计来说,我是不会让Employee知道Task的,这样会导致Employee类迅速膨胀,而且不容易定位方法。
但这里楼主想讨论的是将DAO里面的逻辑放到Domain里来。
这是些什么逻辑呢?难道不就是如何通过某些条件查找到领域的某些对象么?
如果是这种逻辑我认为完全没有必要放入Domain中,因为DAO层负责持久化Domain对象,自然必须知道Domain对象间的关系,查找操作是建立在上述关系之上,我看不出让DAO来干这些有什么不妥。
另外通过加Set的方式,其本身是否只能做一些有关指定对象属性的简单查询,跨对象的条件查询如何处理?
贫血的Domain,贫的血,我想主要是被Service占有了,而不是DAO。
我就搞不明白, 为什么明明很简单清晰的 DAO 类中的方法一定要使用那么多晦涩的技术
把它们搞成所谓的 rich domain model 呢?
脱裤子放屁?
不明白就要好好学习,好好看看以前的帖子。
确实没看出来有什么优点,至少违反了简单性原则。
如果我设计,我连getAsset这样的方法都不放在Employee上。因为
Employee可能能够get的对象很多,都在Employee类上去get,那么Employee类要依赖多少外来的东西?
随着业务不断扩展,更多的对象都会合Employee发生关系,难道要不断修改Employee类来增添新的特性?
为何我们总是乐于把简单的事情复杂化呢?
请问Employee依赖了什么“外来的东西”了?难道修改Employee类来添加新特性就是简单问题复杂化啦?getAsset是什么?
更多对象与Employee发生关系。这个事实本身并没有什么问题。取决于你的domain。如果他们确实与employee关联,那么这种关系的建立正是一种建模的过程。如果domain并没有这样的关联,那可能就是一种建模错误。大家不要树一些这么虚的靶子。不妨举一些实际的例子。我们再来看,“逻辑”应该放在那里。
这个观点不能苟同,一个文件超过1000行,3个人同时去维护这个文件就会造成互相困惑,因为任何一个人在试图加一段逻辑的时候,就得在这1000行中费力的查找,有没有别人已经完成了类似的查询逻辑,或者有那些查询逻辑已经完成,我可以在这个基础上加以扩展。你认为在这种情况下,工作效率和代码质量到底是提高了还是降低了?
任何技术,他要发展并得到大家的任何,必须经过实践,尤其是团队合作的实践,如果一个技术的引入,不仅使得团队合作更加困难,而且降低了团队的工作效率,这种技术我想也是没什么发展前途的。
对于Rich Domain Object,我认为在之前的讨论中,大家都过分执着于这个Rich的含义了。Rich真的是一个非常OO,非常被大家能够接受,并且明显提高生产力的一个东西嘛?我认为不是。对于Java语言本身来说,它并没有Ruby语言与生俱来的优势,要去做一件它本身并不擅长的事情,可能所需要花费的代码远远超过这个事情本身所带来的价值。所以我认为不要再去花时间讨论这种模型,在生产环境上意义不大。
为什么会超过一千行呢?我只是说没有类比Employee更了解自己的ProcessingTasks。你认为什么getProcessingTasks应该放在那里才更具有“大团队的管理性”呢?
另外ruby有什么与生俱来的优势,我们又在用java做一件什么不擅长的事情?能不能给一个具体的例子,我们可以比较一些用ruby和java实现的差别。
这个观点不能苟同,一个文件超过1000行,3个人同时去维护这个文件就会造成互相困惑,因为任何一个人在试图加一段逻辑的时候,就得在这1000行中费力的查找,有没有别人已经完成了类似的查询逻辑,或者有那些查询逻辑已经完成,我可以在这个基础上加以扩展。你认为在这种情况下,工作效率和代码质量到底是提高了还是降低了?
任何技术,他要发展并得到大家的任何,必须经过实践,尤其是团队合作的实践,如果一个技术的引入,不仅使得团队合作更加困难,而且降低了团队的工作效率,这种技术我想也是没什么发展前途的。
对于Rich Domain Object,我认为在之前的讨论中,大家都过分执着于这个Rich的含义了。Rich真的是一个非常OO,非常被大家能够接受,并且明显提高生产力的一个东西嘛?我认为不是。对于Java语言本身来说,它并没有Ruby语言与生俱来的优势,要去做一件它本身并不擅长的事情,可能所需要花费的代码远远超过这个事情本身所带来的价值。所以我认为不要再去花时间讨论这种模型,在生产环境上意义不大。
同意。其实domain对象和数据库绑定,直接用session没啥问题。ActiveRecord不也这么干的嘛。人家连字段都是数据库的。
同意,注入DAO没什么不好。测试的时候起一个内存数据库就好了。再弄几个db fixture,状态准备也一样轻松搞定。
执行业务逻辑就用代码写好了。更新数据就是修改自己的状态。service会去最后把domain对象save回去的,因为hibernate可以遍历对象图找出改动,所以这个我们不用操心。
插入数据可以通过添加元素到一个特殊的RichList来实现。
简单来说,就是把一张表抽象为一个RichList。添加记录就是add,删除记录就是remove。这个RichList在domain中使用,但是domain并不用担心这个list是怎么构建出来的。在单元测试的时候可以使用一个InMemory的List,在产品代码中这个List是一个包装着Hibernate Session的家伙。
nice! 可是我怎么总感觉RichList像个变了性的通用DAO呢,也就是用它将domain与数据持久化代码进行了一次隔离。呵呵
同意,注入DAO没什么不好。测试的时候起一个内存数据库就好了。再弄几个db fixture,状态准备也一样轻松搞定。
执行业务逻辑就用代码写好了。更新数据就是修改自己的状态。service会去最后把domain对象save回去的,因为hibernate可以遍历对象图找出改动,所以这个我们不用操心。
插入数据可以通过添加元素到一个特殊的RichList来实现。
简单来说,就是把一张表抽象为一个RichList。添加记录就是add,删除记录就是remove。这个RichList在domain中使用,但是domain并不用担心这个list是怎么构建出来的。在单元测试的时候可以使用一个InMemory的List,在产品代码中这个List是一个包装着Hibernate Session的家伙。
how about assign method ?
——————————————————————
Domain Model贫血是说属于Domain Model的逻辑没有放在Domain Model中。那是哪些逻辑没有放到Domain Model中,从而导致贫血一说呢?原因有很多,但是我认为最主要是Service中的那些逻辑。而这些逻辑又有一个共同的特点就是依赖于DAO,或者说需要查询数据库。Robbin的帖子:http://www.iteye.com/topic/57075,举了一个很好的例子。我取其中的一个部分在这里做演示用。
public class Employee { private Set<Task> tasks = new HashSet<Task>(); }
public class Task { private String name; private Employee owner; private Date startTime; private Date endTime; }
这是一个很简单的一对多的关系。现在要查找指定员工的处理中的任务。如果忽略数据库的存在,我想大部分的同志都会这么实现:
public class Employee { private Set<Task> tasks = new HashSet<Task>(); public Set<Task> getProcessingTask() { ... } }
这也符合OO数据隐藏的基本原则。但是如果有数据库存在,怎么写就不那么容易决定了。如果没有Hibernate这样的ORM。那肯定是:
public class TaskDAO { public Set<Task> getProcessingTasks(Employee employee) { ...//sql } }
那我觉得,这就导致了Domain Model的失血。因为没有数据库的时候,这这个方法本来应该在Employee上的,而不是在DAO上的。
如果有Hibernate呢?是不是我就可以把这段代码写到Employee里面去呢?
@Entity public class Employee { @OneToMany private Set<Task> tasks = new HashSet<Task>(); public Set<Task> getProcessingTask() { ... } }
还是有问题。因为访问tasks的时候,Hibernate会去加载数据。getProcessingTask会便利所有的task。如果task的数量很多,这降极大的影响性能。所以为了能够享受到关系数据库查询速度的好处,我们要还要利用SQL。于是DAO又再次地找到了自己的位置。那么怎么解决这个问题呢?在http://www.iteye.com/topic/57075的回帖中nihongye同学提出了一个解决方案。本质来说就是不让hibernate来映射tasks,改由查询来获得。加上Spring支持的@Configurable标记,我们可以把代码写成这样
@Entity @Configurable public class Employee { private TaskDao dao; public Set<Task> getProcessingTask() { return dao.getProcessingTask(this); } public void setTaskDao(TaskDao dao) { this.dao = dao; } }
我们当然还可以把TaskDao替换成变的形式。比如http://www.iteye.com/topic/65406里firebody提到的那样。但是本质上来说,都是让Employee能够直接去使用Hibernate做查询。但是坏处是给Domain纯净分子的口实。虽然,我认为和ActiveRecord类似,entity绑定在数据库上没啥不好。另外一个缺点就是,要么仍然有一个Dao来封装查询逻辑的实现,要么Employee的实现中出现太多的hibernate api,而且写法复杂。这也就是Robbin一再强调,ActiveRecord那样的api在Java世界中不是不可以,而是实现复杂难度高的原因。注入可以解决问题,但是对Hibernate的依赖强而且写法丑陋。
那么有没有更优美的方案呢?有:
public class Employee { private RichSet<Task> tasks = new DefaultRichSet<Task>(); public RichSet<Task> getProcessingTasks() { return tasks.find("startTime").le(new Date()).find("endTime").isNull(); } ... }
RichSet是我自己编造的一个名字。它是一个”rich“的set。其实就是附加了一些find,sort,sum之类的操作。
public interface RichSet<T> extends Set<T> { Finder<RichSet<T>> find(String expression); int sum(String expression); }
DefaultRichSet是这些附加操作的内存版本的实现。这个能解决问题么?还是不能,这时候getProcessingTasks的时候,richSet还是去遍历内部的_tasks,然后把结果过滤出来。而且,hibernate还拒绝接受这样set。为了让hibernate能够接受RichSet,我们需要这么写配置文件。
<hibernate-mapping default-access="field" package="net.sf.ferrum.example.domain"> <class name="Employee"> <tuplizer entity-mode="pojo" class="net.sf.ferrum.RichEntityTuplizer"/> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="salary"/> <many-to-one name="department"/> <set name="tasks" cascade="all" inverse="true" lazy="true"> <key/> <one-to-many class="Task" /> </set> </class> </hibernate-mapping>
通过指定RichEntityTuplizer,我们可以控制Hibernate的动态增强过程。
public class RichEntityTuplizer extends PojoEntityTuplizer { public RichEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) { super(entityMetamodel, mappedEntity); } protected Setter buildPropertySetter(final Property mappedProperty, PersistentClass mappedEntity) { final Setter setter = super.buildPropertySetter(mappedProperty, mappedEntity); if (!(mappedProperty.getValue() instanceof org.hibernate.mapping.Set)) { return setter; } return new Setter() { public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException { Object wrappedValue = value; if (value instanceof Set) { HibernateRepository repository = new HibernateRepository(); repository.setSessionFactory(factory); wrappedValue = new HibernateRichSet((Set) value, repository, getCriteria(mappedProperty, target)); } setter.set(target, wrappedValue, factory); } public String getMethodName() { return setter.getMethodName(); } public Method getMethod() { return setter.getMethod(); } }; } }
这样,tasks就不再是DefaultRichSet了。Hibernate会尝试去增强为PersisentSet,但是被RichEntityTuplizer改写为增强HibernateRichSet了。这样就形成了HibernateRichSet -> PersisentSet -> DefaultRichSet -> HashSet 的包含关系。
当用户尝试在tasks上做find的时候,就不再是DefaultRichSet来做collection遍历了,而是HibernateRichSet去拼装一个DetachedCriteria。最后当用户在查询的结果上取size()或者取具体元素的时候,这个criteria被拿去求值。
通过使用RichSet,domain model具有了对自身进行查询的能力。更重要的是,这种能力的获得,不是通过把Hibernate session注入到domain model中。domain仍然是纯净的,没有依赖于数据库的东西。而且domain是可以脱离容器使用的。new Employee出来就可以直接使用,测试。区别只是经过repository增强的entity会使用sql,而transient的entity所有的查询都是通过遍历实现的。
没有了DAO之后,Domain Model是不是能够摆脱贫血的困扰呢?这个还需要观察。不过我认为至少是向前迈了一步了。
- ferrum.zip (90.6 KB)
- 描述: 新版代码,废除了手工的增强,改由Tuplizer来搭Hibernate的顺风车。
- 下载次数: 302
评论
37 楼
Norther
2008-05-10
楼主的精神值得鼓励,期望更进一步的实践。
另外大家又回到去讨论domain object是否有意义的问题上了,这个问题很久以前已经讨论过了,还是那句话,去看以前的帖子吧。
另外大家又回到去讨论domain object是否有意义的问题上了,这个问题很久以前已经讨论过了,还是那句话,去看以前的帖子吧。
36 楼
rrsy23
2008-05-10
感觉大家就在讨论:宝马与QQ那个好?
其实所谓的Domain,Dao不就是个概念,是个名词!
你用jsp直接访问数据库,还不是把字段通过JDBC写到数据库,你说也没有dao,也许没有,因为不明显。如果加个类,把数据持久分离如何?
所谓DAO其实无论如何都有,只是开发时间程序员关心与否,对一些中小mis系统,你可以把dao直接让业务逻辑基extends,直接crud domain这样也没有到
一句话:
世界就是:表面没有,其实均有,看着简单,底层复杂!
简单唯美,适用是道,存在之智,万事两面!
唯美是佳,过度设计,也需提放,思思雨雨!
其实所谓的Domain,Dao不就是个概念,是个名词!
你用jsp直接访问数据库,还不是把字段通过JDBC写到数据库,你说也没有dao,也许没有,因为不明显。如果加个类,把数据持久分离如何?
所谓DAO其实无论如何都有,只是开发时间程序员关心与否,对一些中小mis系统,你可以把dao直接让业务逻辑基extends,直接crud domain这样也没有到
一句话:
世界就是:表面没有,其实均有,看着简单,底层复杂!
简单唯美,适用是道,存在之智,万事两面!
唯美是佳,过度设计,也需提放,思思雨雨!
35 楼
partech
2008-05-10
public class Employee { private Set<Task> tasks = new HashSet<Task>(); public Set<Task> getProcessingTask() { ... } }
确实就实际设计来说,我是不会让Employee知道Task的,这样会导致Employee类迅速膨胀,而且不容易定位方法。
public class Employee { } public class Task{ public static TaskFinder taskFinder; private Employee owner; public Employee getOwner() { return employee; } }
Task.taskFinder.findBy(employee); ... Task.taskFinder.findBy(employee,startTime,endTime);
但这里楼主想讨论的是将DAO里面的逻辑放到Domain里来。
这是些什么逻辑呢?难道不就是如何通过某些条件查找到领域的某些对象么?
如果是这种逻辑我认为完全没有必要放入Domain中,因为DAO层负责持久化Domain对象,自然必须知道Domain对象间的关系,查找操作是建立在上述关系之上,我看不出让DAO来干这些有什么不妥。
另外通过加Set的方式,其本身是否只能做一些有关指定对象属性的简单查询,跨对象的条件查询如何处理?
贫血的Domain,贫的血,我想主要是被Service占有了,而不是DAO。
34 楼
taowen
2008-05-10
删除。。。编辑错了
33 楼
taowen
2008-05-10
ltian 写道
Norther 写道
coolnight 写道
我就搞不明白, 为什么明明很简单清晰的 DAO 类中的方法一定要使用那么多晦涩的技术
把它们搞成所谓的 rich domain model 呢?
脱裤子放屁?
不明白就要好好学习,好好看看以前的帖子。
确实没看出来有什么优点,至少违反了简单性原则。
如果我设计,我连getAsset这样的方法都不放在Employee上。因为
Employee可能能够get的对象很多,都在Employee类上去get,那么Employee类要依赖多少外来的东西?
随着业务不断扩展,更多的对象都会合Employee发生关系,难道要不断修改Employee类来增添新的特性?
为何我们总是乐于把简单的事情复杂化呢?
请问Employee依赖了什么“外来的东西”了?难道修改Employee类来添加新特性就是简单问题复杂化啦?getAsset是什么?
更多对象与Employee发生关系。这个事实本身并没有什么问题。取决于你的domain。如果他们确实与employee关联,那么这种关系的建立正是一种建模的过程。如果domain并没有这样的关联,那可能就是一种建模错误。大家不要树一些这么虚的靶子。不妨举一些实际的例子。我们再来看,“逻辑”应该放在那里。
32 楼
taowen
2008-05-10
downpour 写道
taowen 写道
查询逻辑,比如说对一个collection的过滤是应该封装在domain中的。持久化逻辑并不是什么逻辑。持久化只是一种domain的存在形式。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。
这个观点不能苟同,一个文件超过1000行,3个人同时去维护这个文件就会造成互相困惑,因为任何一个人在试图加一段逻辑的时候,就得在这1000行中费力的查找,有没有别人已经完成了类似的查询逻辑,或者有那些查询逻辑已经完成,我可以在这个基础上加以扩展。你认为在这种情况下,工作效率和代码质量到底是提高了还是降低了?
任何技术,他要发展并得到大家的任何,必须经过实践,尤其是团队合作的实践,如果一个技术的引入,不仅使得团队合作更加困难,而且降低了团队的工作效率,这种技术我想也是没什么发展前途的。
对于Rich Domain Object,我认为在之前的讨论中,大家都过分执着于这个Rich的含义了。Rich真的是一个非常OO,非常被大家能够接受,并且明显提高生产力的一个东西嘛?我认为不是。对于Java语言本身来说,它并没有Ruby语言与生俱来的优势,要去做一件它本身并不擅长的事情,可能所需要花费的代码远远超过这个事情本身所带来的价值。所以我认为不要再去花时间讨论这种模型,在生产环境上意义不大。
为什么会超过一千行呢?我只是说没有类比Employee更了解自己的ProcessingTasks。你认为什么getProcessingTasks应该放在那里才更具有“大团队的管理性”呢?
另外ruby有什么与生俱来的优势,我们又在用java做一件什么不擅长的事情?能不能给一个具体的例子,我们可以比较一些用ruby和java实现的差别。
31 楼
downpour
2008-05-10
taowen 写道
查询逻辑,比如说对一个collection的过滤是应该封装在domain中的。持久化逻辑并不是什么逻辑。持久化只是一种domain的存在形式。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。
这个观点不能苟同,一个文件超过1000行,3个人同时去维护这个文件就会造成互相困惑,因为任何一个人在试图加一段逻辑的时候,就得在这1000行中费力的查找,有没有别人已经完成了类似的查询逻辑,或者有那些查询逻辑已经完成,我可以在这个基础上加以扩展。你认为在这种情况下,工作效率和代码质量到底是提高了还是降低了?
任何技术,他要发展并得到大家的任何,必须经过实践,尤其是团队合作的实践,如果一个技术的引入,不仅使得团队合作更加困难,而且降低了团队的工作效率,这种技术我想也是没什么发展前途的。
对于Rich Domain Object,我认为在之前的讨论中,大家都过分执着于这个Rich的含义了。Rich真的是一个非常OO,非常被大家能够接受,并且明显提高生产力的一个东西嘛?我认为不是。对于Java语言本身来说,它并没有Ruby语言与生俱来的优势,要去做一件它本身并不擅长的事情,可能所需要花费的代码远远超过这个事情本身所带来的价值。所以我认为不要再去花时间讨论这种模型,在生产环境上意义不大。
30 楼
taowen
2008-05-10
nihongye 写道
按照马丁的意思,Rich Domain Object无非是想程序更好维护,那我想,既然这样想了,那就直奔主题,不要被隔离的思想束缚住,Domain Object就直接引用hibernate session或者dao甚至jdbc connection,就好了.看看达到这个更好维护的目的没有。老是想着隔离,隐藏,呢,创建出一些新的隔离对象,干扰了视线。
同意。其实domain对象和数据库绑定,直接用session没啥问题。ActiveRecord不也这么干的嘛。人家连字段都是数据库的。
29 楼
taowen
2008-05-10
查询逻辑,比如说对一个collection的过滤是应该封装在domain中的。持久化逻辑并不是什么逻辑。持久化只是一种domain的存在形式。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。
关于Domain对象太大这个问题,你说有什么问题呢?要具体问题具体分析吧。团队合作,更加超出了技术能够解决的范畴了。
28 楼
nihongye
2008-05-10
按照马丁的意思,Rich Domain Object无非是想程序更好维护,那我想,既然这样想了,那就直奔主题,不要被隔离的思想束缚住,Domain Object就直接引用hibernate session或者dao甚至jdbc connection,就好了.看看达到这个更好维护的目的没有。老是想着隔离,隐藏,呢,创建出一些新的隔离对象,干扰了视线。
27 楼
downpour
2008-05-10
另一个方面的问题:一个主业务Domain Object,其中可能包含20来个字段,可能需要和多个其他的Domain Object关联。这个时候,按照Rich Domain Object的理论和你的实现,可以把这些查询逻辑或者持久化逻辑全部封装在Domain Object的内部。那么这个Domain Object会有多少行?在团队合作中,如何保持它内部语义的不重复性?
26 楼
taowen
2008-05-10
对啊,就是在Domain <-> Hibernate之间建立了一个隔离层。只不过它本身是一个collection而已。其实自身是一个generic dao。
25 楼
ronghao
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与数据持久化代码进行了一次隔离。呵呵
24 楼
Feiing
2008-05-10
got it, thanks
23 楼
taowen
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.
引用
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.
22 楼
taowen
2008-05-10
ronghao 写道
在我实际的编程里,Domain对象里大量注入了DAO,我并没有觉得有什么不好,好处在于带来了一致的编程体验,唯一的问题在于给测试带来了麻烦,单元测试需要写很多stub。在特定于工作流引擎的情况下,很多同事主张用事件的方式来解除Domain对象与DAO的耦合,但是我觉得没有必要。
楼主的解决方案我觉得还是存在一定局限性的。比如如果Domain对象的业务方法需要执行业务逻辑并插入或更新数据时。
楼主的解决方案我觉得还是存在一定局限性的。比如如果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的家伙。
21 楼
taowen
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); } }
20 楼
Feiing
2008-05-10
taowen 写道
public class InMemoryEmployeeTest { @Test public void processing_task_should_be_started_but_not_ended() { Employee taowen = new Employee("taowen", 1000, new Department("R&D")); Task processedTask = taowen.assign("develop jfixture framework"); processedTask.finish(processedTask.getStartTime()); taowen.assign("develop ferrum framework1"); taowen.assign("develop ferrum framework2"); taowen.assign("develop ferrum framework3"); taowen.assign("develop ferrum framework4"); taowen.assign("develop ferrum framework5"); taowen.assign("develop ferrum framework6"); taowen.assign("develop ferrum framework7"); taowen.assign("develop ferrum framework8"); Assert.assertEquals(8, taowen.getProcessingTasks().size()); } }
how about assign method ?
19 楼
ronghao
2008-05-10
在我实际的编程里,Domain对象里大量注入了DAO,我并没有觉得有什么不好,好处在于带来了一致的编程体验,唯一的问题在于给测试带来了麻烦,单元测试需要写很多stub。在特定于工作流引擎的情况下,很多同事主张用事件的方式来解除Domain对象与DAO的耦合,但是我觉得没有必要。
楼主的解决方案我觉得还是存在一定局限性的。比如如果Domain对象的业务方法需要执行业务逻辑并插入或更新数据时。
楼主的解决方案我觉得还是存在一定局限性的。比如如果Domain对象的业务方法需要执行业务逻辑并插入或更新数据时。
18 楼
taowen
2008-05-10
public class InMemoryEmployeeTest { @Test public void processing_task_should_be_started_but_not_ended() { Employee taowen = new Employee("taowen", 1000, new Department("R&D")); Task processedTask = taowen.assign("develop jfixture framework"); processedTask.finish(processedTask.getStartTime()); taowen.assign("develop ferrum framework1"); taowen.assign("develop ferrum framework2"); taowen.assign("develop ferrum framework3"); taowen.assign("develop ferrum framework4"); taowen.assign("develop ferrum framework5"); taowen.assign("develop ferrum framework6"); taowen.assign("develop ferrum framework7"); taowen.assign("develop ferrum framework8"); Assert.assertEquals(8, taowen.getProcessingTasks().size()); } }
发表评论
-
Guice这高级货
2009-06-15 15:17 6934Guice在大部分时间都是很方便的,简单易用。Guice和Sp ... -
再论领域模型的困境
2009-06-03 18:54 2076距离上次发帖讨论领域 ... -
让AnnotationConfiguration支持自定义UserCollectionType
2008-12-09 17:45 2787在Hibernate的Jira上,这个两个issue已经放了很 ... -
领域模型的价值与困境
2008-11-27 23:23 3950很久以前大家就关于这个方面有很多讨论了。前两天我又挖了一个坑来 ... -
你所不知道的CommandBar
2008-10-29 08:40 2913Office能够让你写插件。2 ... -
关于Estimation的随笔
2008-10-27 09:04 1140Estimation有很多流派。 从数字的选择上来看:有的人喜 ... -
Domain Model - The Theory, The Reality, The Dream
2008-08-10 21:25 1285梦想照进现实 -
关于estimation的闲言碎语
2008-07-11 09:06 1507estimation只是一个开始,不是结束.好的estim ... -
let's placeBid
2008-05-12 10:11 1510这个例子很老啦,在之 ... -
图形界面自动化测试
2008-05-04 22:16 2063Windows Win32 API (pywinauto, a ... -
the paint points of xaml
2008-01-16 08:56 1494Pain Point 1: XAML always creat ... -
lessons we have learnt about office integration
2008-01-16 08:54 1425Lesson 1: trust it Everything ... -
Outlook MAPIOBJECT
2008-01-09 18:06 2076Outlook的对象模型中,很多对象都有一个MAPIOBJEC ... -
.NET Remoting Callback
2007-10-12 20:42 2237有三个主要的障碍: 1、服务器解析不到客户端的assembly ... -
企业应用开发者使用WPF的三个理由
2007-05-16 23:02 5760让控件更灵活的Data Template ... -
用UIAutomation做验收测试
2007-05-16 22:19 4555这是被测的应用程序: 应用.NET 3.0提供的UIA ... -
mock框架搞什么搞?
2007-05-11 11:11 13525今天早上一时兴起,去网上下载下来JMock,EasyMock最 ... -
主动重构 => 被动重构
2007-05-10 16:34 2008引言 最近杂七杂八地思考了不少东西。但是很惊异地发现这三三两 ... -
我的酒窝.NET
2007-04-30 16:59 2766ajoo同学的酒窝有.NET版本啦! 项目主页: http: ... -
Naive Container 发布1.0版本
2007-04-29 17:50 2591二进制文件和源代码可以从这里下载到: http://naive ...
相关推荐
贫血模型(Anemic Domain Model)源自于传统的MVC(Model-View-Controller)架构,其中模型部分通常是数据容器,包含了属性但很少有或没有业务逻辑。业务逻辑通常被分离到服务层或者控制器中,这样做的好处是使得...
为了解决贫血模型的问题,开发者可能会转向富模型(Rich Domain Model)或领域驱动设计(Domain-Driven Design, DDD)。在DDD中,业务规则和逻辑被封装到领域对象(Domain Objects)中,这些对象不仅包含数据,还...
领域模型(Domain Model)和贫血模型(Anemic Domain Model)是两种常见的模型设计模式,它们各有特点,适用于不同的场景。本资料包旨在通过实例对比,帮助初学者理解这两种模型的区别和概念,并提供实际的Java代码...
- 采用领域驱动设计的实践,比如贫血模型(Anemic Domain Model)和富领域模型(Rich Domain Model),以及Repository模式来管理领域对象。 通过上述分析,我们可以看到领域模型设计的关键在于理解业务逻辑,将其...
16. 领域模型(Domain Model)和贫血模型(Anaemic Domain Model)与充血模型(Rich Domain Model)的区别在于,贫血模型将业务逻辑放在服务层,而充血模型则将业务逻辑放在领域模型的实体中。 17. 测试驱动开发...
这个系统利用了贫血模型(Anemic Domain Model)来实现DAO层,旨在提供高效、稳定且易于维护的企业级解决方案。 首先,Struts2是一个强大的MVC(Model-View-Controller)框架,它负责处理HTTP请求,并将这些请求...
例如,开发者可以决定是否生成贫血模型(Anemic Domain Model)或富模型(Rich Domain Model),是否使用MyBatis或Hibernate作为持久层框架,以及选择RESTful风格的Controller还是传统的Action风格。 此外,自动...
例如,使用贫血模型(Anemic Domain Model)或富领域模型(Rich Domain Model),根据项目需求选择合适的数据访问模式,以及合理地组织Mapper接口和实体类,使代码结构清晰。 总的来说,这个案例涉及了Mybatis注解...
- EJB2.0的设计通常遵循一些模式,如贫血模型(Anemic Domain Model)和富模型(Rich Domain Model)。理解这些模式可以帮助我们更好地分析HCPC管理系统中的设计决策。 8. 源码学习: - 对于"jkjkljk解决好家伙...
12. **贫血模型和富模型(Anemic Domain Model and Rich Domain Model)** 贫血模型将业务逻辑放在服务层,而模型对象仅包含数据。富模型则将业务逻辑内聚到域对象中,提高代码的封装性和可维护性。 通过理解和...
学习Struts不仅要知道如何使用,还要理解如何遵循最佳实践,如分离表现层和业务层,使用贫血模型(Anemic Domain Model),以及如何有效地组织和管理ActionForm对象。 10. **实战案例** 书中可能包含多个实战案例...
8. **最佳实践**:在实际开发中,应遵循一定的设计原则,如贫血模型(Anemic Domain Model)和富领域模型(Rich Domain Model),以及合理的分层架构,以保持代码的可维护性和扩展性。 综上所述,"springMVC整合...
- **解决现实世界的问题**:在开发过程中,开发者需要将现实世界中的实体抽象成模型,即Domain Object。这些模型可以是简单的数据载体(贫血模型),也可以包含业务逻辑(富血模型)。无论哪种形式,模型都无法直接...
使用Struts开发时,应遵循一些最佳实践,例如:明确分离业务逻辑和表现层,使用贫血模型(Anemic Domain Model),合理组织Action和ActionForm,以及充分利用拦截器来增强功能。 综上所述,Struts框架通过其强大的...
1. **领域模型**(Domain Model):领域模型是业务逻辑的抽象,它包含了业务实体、值对象、领域事件、聚合、工厂、仓储等元素。这些元素共同构成了一个反映业务规则的模型。 2. **统一语言**(Ubiquitous Language...
8. **贫血模型与领域模型** - 讨论了一个简单的示例,比较了贫血模型(Anemic Domain Model)和领域模型(Domain-Driven Design)在Java应用中的应用和优缺点。 9. **Spring中的DAO与Service** - 在Spring框架中,...
3. **贫血模型(Anemic Domain Model)**:在生成的代码中,Entity类通常只包含属性,没有业务逻辑方法,这种设计被称为贫血模型,便于保持模型的简单性和纯粹性,但可能需要额外的服务层来处理业务逻辑。...
查询模型通常使用贫血模型(Anemic Domain Model),其中对象主要包含数据字段,不包含业务逻辑。这些模型可以映射到视图层,用于展示数据。例如,我们可能会有一个`UserReadModel`,只包含用户的基本信息,用于检索...
11. **最佳实践**:在实际项目中,应遵循一些最佳实践,例如避免在Action类中使用静态变量,使用贫血模型(Anemic Domain Model)来分离业务逻辑,以及充分利用拦截器来封装共性功能。 12. **安全性**:Struts2的...