从贫血到充血Domain Model

来源:互联网 发布:非农数据公布时间电视 编辑:程序博客网 时间:2024/06/02 12:17

关于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。

最后: 注意到代码中有这么一行

WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO");

确实是一个bad smell.当代码中大量出现后,这种造型是很恐怖的。所以采取了一种处理方式:给所有Domain Model继承一个父类,在父类里集中管理所有Domain Model所依赖的services,在父类里进行造型。

http://www.blogjava.net/ronghao 荣浩原创,转载请注明出处:)  

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信投票地区限制怎么办 微信退款未到账怎么办 微信钱包锁忘了怎么办 xp系统管理员密码忘了怎么办 沃尔沃menu键没反应怎么办 微信转账受限制怎么办 被米虎网骗了手里还有合同怎么办 学校要求上传论文pdf格式怎么办 备份的Wifi密码查看乱码怎么办 u盘密码忘了怎么办 京东会员号被黑怎么办 淘宝企业店铺三证不合一怎么办 淘宝企业店铺营业执照注销了怎么办 不想开淘宝企业店铺了怎么办 id图片跨页排版怎么办 合约机不想要了怎么办? 移动A3手机老卡怎么办 中国移动手机a3很卡怎么办 移动手机a4好卡怎么办 红米手机卡顿反应慢怎么办 红米3s网速慢怎么办 红米4a内存不足怎么办 红米3s手机发热怎么办 魅蓝s6信号差怎么办 oppo手机媒体音量没声音怎么办 红米note3反应慢怎么办 红米4g信号差怎么办 红米4g网速慢怎么办 红米24g信号不好怎么办 红米54g信号不稳定怎么办 红米4a玩游戏卡怎么办 红米4x卡顿怎么办 红米主板烧了怎么办 红米3按键失灵怎么办 l安卓手机运存不够用怎么办 红米2屏幕失灵怎么办 红米手机电池不耐用怎么办 红米手机没内存怎么办 红米2a卡顿怎么办 红米2a手机卡顿怎么办 红米5a内存不足怎么办