- 浏览: 460950 次
- 性别:
- 来自: 北京
-
博客专栏
-
-
张小庆,在路上
浏览量:8952
文章分类
最新评论
-
bad_brain:
很好的文章,帮助我快速了解zookeeper提供的能力以及为什 ...
Zookeeper与paxos算法 -
ixu:
支持,已经买了 是对工作流和BPM的很好总结啊
无知者无畏,一本写了四年的书 -
yangsong158:
看来,我与这个时代有些脱节了。必需加快赶上来。谢谢你的奉献。必 ...
无知者无畏,一本写了四年的书 -
黄粱一梦11:
目标 人没有目标就很容易迷失自己,常常陷入困惑中
PM成长日记第二话-一定要想清楚自己要什么 -
fenian_zhq:
支持。就凭你这个感悟,必须买一本收藏!
无知者无畏,一本写了四年的书
关于Domain Model的讨论已经非常多了,炒炒冷饭,这里是自己的一些做法。
以Workitem(工作流里的工作项)作为例子。
最开始的做法:
一个实体类叫做Workitem,指的是一个工作项或者称为任务项
一个DAO类叫做WorkitemDao
一个业务逻辑类叫做WorkitemManager(或者叫做WorkitemService)
主要看看WorkitemManager,因为主要逻辑集中在这里
public class WorkitemManager { private WorkItemDAO workItemDAO; public void setWorkItemDAO(WorkItemDAO workItemDAO) { this.workItemDAO = workItemDAO; } /** * 提交工作项 * @param workitemId 工作项ID */ public void commitWorkitem(String workitemId){ WorkItem workitem = workItemDAO.getWorkItem(workitemId); //当前工作项结束 workitem.complete(); int sID = workitem.getSequenceId(); //找到所对应的节点 InstActivity instActivity=workitem.getInstActivity(); //查找是否存在下一工作项 WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1); //如果不存在则触发节点流转 if (sequenceWorkitem == null) { instActivity.signal(); } //否则把下一工作项激活 else { sequenceWorkitem.setExecutive(); } } }
Workitem类里有一些状态转换的逻辑,这样避免直接调用get/set属性方法
public class Workitem{ private int state = WorkitemInfo.PREPARE; /** * 委派工作项 */ public void commission() { if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED && state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND) throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMMISSIONED); setCommitted(new Timestamp(System.currentTimeMillis())); } /** * 完成工作项 */ public void complete() { if (state != WorkitemInfo.SIGNINED) throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMPLETE); setCompleted(new Timestamp(System.currentTimeMillis())); } }
接下来的做法:
三个类不变,将WorkitemManager打平,将逻辑移动到Workitem
public class WorkitemManager { private WorkItemDAO workItemDAO; public void setWorkItemDAO(WorkItemDAO workItemDAO) { this.workItemDAO = workItemDAO; } /** * 提交工作项 * @param workitemId 工作项ID */ public void commitWorkitem(String workitemId){ WorkItem workitem = workItemDAO.getWorkItem(workitemId); //当前工作项提交 workitem.commit(); } }
实际上此时WorkitemManager的功能非常有限,仅仅是事务边界和获取workitem对象,甚至在一些情况下可以省略。
通过一个Container类将spring的applicationContext进行封装,然后通过getBean()的静态方法即可访问被spring所管理的bean。实际是将workItemDAO隐式注入了Workitem。
public class Workitem{ /** * 提交工作项 */ public void commit() { if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED && state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND) throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMMISSIONED); setCommitted(new Timestamp(System.currentTimeMillis())); int sID = workitem.getSequenceId(); WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO"); //查找是否存在下一工作项 WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1); //如果不存在则触发节点流转 if (sequenceWorkitem == null) { instActivity.signal(); } //否则把下一工作项激活 else { sequenceWorkitem.setExecutive(); } } }
这样带来的好处是业务逻辑全部被封装到Domain Model,Domain Model之间的交互变得非常的简单,没有频繁的set/get,直接调用有业务语义的Domain Model的方法即可。问题在于单元测试时脱离不了spring的容器,workItemDAO需要stub。我觉得这个问题不大,反而是Domain Model开始变得臃肿,在业务逻辑复杂时代码行急剧膨胀。
现在的做法
以上三个类保持不变,增加一个类WorkitemExecutor,将业务逻辑移步。
public class Workitem{ /** * 提交工作项 */ public void commit() { if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED && state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND) throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE); setState(WorkitemInfo.COMMISSIONED); setCommitted(new Timestamp(System.currentTimeMillis())); WorkitemExecutor workitemExecutor=(WorkitemExecutor)Container.getBean("workitemExecutor"); workitemExecutor.commitWorkitem(this); } }
public class WorkitemExecutor { private WorkItemDAO workItemDAO; public void setWorkItemDAO(WorkItemDAO workItemDAO) { this.workItemDAO = workItemDAO; } /** * 提交工作项 * @param workitemId 工作项ID */ public void commitWorkitem(Workitem workitem){ int sID = workitem.getSequenceId(); //找到所对应的节点 InstActivity instActivity=workitem.getInstActivity(); //查找是否存在下一工作项 WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1); //如果不存在则触发节点流转 if (sequenceWorkitem == null) { instActivity.signal(); } //否则把下一工作项激活 else { sequenceWorkitem.setExecutive(); } } }
将业务逻辑拆分成两部分,一部分在Workitem,另一部分委托给WorkitemExecutor。实际上是Domain Model将复杂逻辑的情况重新外包出去。调用的时候,面向的接口还是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。实际可以这样认为,传统的方式
Client->(Business Facade)->service(Business Logic 部分依赖Domain Model)->Data Access(DAO)。
现在的方式
Client->(Business Facade)->Domain Model->service->Data Access(DAO)。
另外,在返回client端的查询的时候还是倾向于直接调用DAO,而不是通过Domain Model。
评论
其实上只是颠倒了下调用的位置,以前我需要调用service,现在只要调用domain 即可。但是实际应用中,我的代码中具有大量domain,它们如果去调用service会非常bt,现在直接调用相关domain,而且这些相关domain是hibernate帮我注入的,这样用起来就爽的多了。代码也更容易理解。
在服务层中为贫血对象充血(装备逻辑方法)。
但自从深度使用动态语言后,才发现自己被Java害了
曾经一个不是牛人的牛人对我说过,面向对象只是一个思想
是解决逻辑问题的一个思路,不可能在代码中完美的体现OO,
现在想来,到底Java和JavaScript谁更OO呢?
也不好吧 实体类很可能会出现继承的情况,我觉得不如把实体类包装一下,合理的运用一下泛型,会省很多事
Agree!
very very very bad smell. : )
why?

very very very bad smell. : )
发表评论
-
Zookeeper与paxos算法
2012-03-22 20:36 16862一、 zookeeper是什么 ... -
关于异常的问与答
2010-09-16 22:34 1160今天的问题是关于异常 ... -
一家公司发展的胡言乱语
2010-01-16 23:06 2933终于一天早上,睁开极 ... -
数据驱动测试
2009-12-05 22:25 4250我们从一个最简单的登录例子开始。最开始我们需要验证在用户名和密 ... -
《Head First Process-深入浅出流程》连载预告
2009-10-17 22:58 6238似乎一到年末,就会忙 ... -
也说炮轰
2009-10-05 13:13 1969社区里目前最火的无疑 ... -
使用selenium测试showModalDialog模态对话框
2009-07-27 21:10 7090Selenium目前没有提供对IE模态对话框(即通过showM ... -
(Multi-stage Continuous Integration)多阶段持续集成
2009-05-26 23:08 1262一、目前的情况 目前我 ... -
JbpmSide 流程设计器进度
2009-03-26 22:15 4465汇报一下设计器当前进度以及下一阶段主要的开发目标。 当前进度主 ... -
jBPM-side流程设计器功能规划
2009-03-08 21:57 2271目标: jBPM-side ProcessDesigne ... -
Flex框架Riawave应用以及对AJAX开发框架的思考
2009-03-01 22:05 1233Jbpmside要使用Flex开发流 ... -
你服务,你全家才服务
2009-02-19 14:18 1702在拥挤的公交车上读完《工作流管理(模型、方法和系统)》,自从搬 ... -
工作流技术基础读后
2009-02-09 18:03 1704大概花了三天的时间读完这本书,书本身也 ... -
BPM向左,工作流向右(二)工作流系统杂谈
2008-11-07 11:28 2344当 面对一个完整的工作流系统时,你可能会被它众多的功能所困惑: ... -
基于memcached的SNA实现
2008-10-28 17:34 3748系统要集群,使用SNA方案。一、 缓存的处理缓存要使用统一的缓 ... -
SNA方案之session炒冷饭
2008-09-04 14:49 2103SNA方案中,session的处理是一个重要方面。原帖见这里: ... -
一次性能调优的实战
2008-09-01 12:56 1937项目情况:是一个大型 ... -
BPM向左,工作流向右(一)什么是业务流程
2008-08-26 17:43 2432从事工作流以及相关开 ... -
js组件的测试,是个问题
2008-08-11 19:06 1122用js编写自己的组件,测试一直是个头疼的问题。最开始大量使用a ... -
工作流之收回
2008-07-15 18:31 1611收回 收回是工作 ...
相关推荐
为了帮助开发者更深入地理解和应用这两种模型,可以参考一些详细文档,例如"贫血充血00.doc"。这类文档通常会详细介绍贫血和充血模型的概念、优缺点、适用场景以及实际应用案例。通过阅读这些资料,开发者可以更好地...
充血模型,也被称为“Rich Domain Model”,是领域驱动设计(DDD)中的一种核心概念。在软件开发中,领域模型是对业务领域的抽象和建模,它包含业务规则、逻辑和状态。充血模型强调对象应该拥有自己的行为和状态,而...
接下来,我们谈论“充血的领域模型”(Rich Domain Model)。在传统的贫血模型中,领域对象通常只包含数据,而业务逻辑则分散在服务层或其他地方。然而,在充血模型中,领域对象不仅包含了数据,还封装了大量的业务...
领域模型(Domain Model)和贫血模型(Anemic Domain Model)是两种常见的模型设计模式,它们各有特点,适用于不同的场景。本资料包旨在通过实例对比,帮助初学者理解这两种模型的区别和概念,并提供实际的Java代码...
16. 领域模型(Domain Model)和贫血模型(Anaemic Domain Model)与充血模型(Rich Domain Model)的区别在于,贫血模型将业务逻辑放在服务层,而充血模型则将业务逻辑放在领域模型的实体中。 17. 测试驱动开发...
1. **领域模型**(Domain Model):领域模型是业务逻辑的抽象,它包含了业务实体、值对象、领域事件、聚合、工厂、仓储等元素。这些元素共同构成了一个反映业务规则的模型。 2. **统一语言**(Ubiquitous Language...