關(guān)于Domain Model的討論已經(jīng)非常多了,炒炒冷飯,這里是自己的一些做法。
以Workitem(工作流里的工作項(xiàng))作為例子
最開始的做法:
一個(gè)實(shí)體類叫做Workitem,指的是一個(gè)工作項(xiàng)或者稱為任務(wù)項(xiàng)
一個(gè)DAO類叫做WorkitemDao
一個(gè)業(yè)務(wù)邏輯類叫做WorkitemManager(或者叫做WorkitemService)
主要看看WorkitemManager,因?yàn)橹饕壿嫾性谶@里
public class WorkitemManager {
private WorkItemDAO workItemDAO;
public void setWorkItemDAO(WorkItemDAO workItemDAO) {
this.workItemDAO = workItemDAO;
}
/**
* 提交工作項(xiàng)
* @param workitemId 工作項(xiàng)ID
*/
public void commitWorkitem(String workitemId){
WorkItem workitem = workItemDAO.getWorkItem(workitemId);
//當(dāng)前工作項(xiàng)結(jié)束
workitem.complete();
int sID = workitem.getSequenceId();
//找到所對應(yīng)的節(jié)點(diǎn)
InstActivity instActivity=workitem.getInstActivity();
//查找是否存在下一工作項(xiàng)
WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
//如果不存在則觸發(fā)節(jié)點(diǎn)流轉(zhuǎn)
if (sequenceWorkitem == null) {
instActivity.signal();
}
//否則把下一工作項(xiàng)激活
else {
sequenceWorkitem.setExecutive();
}
}
}
Workitem類里有一些狀態(tài)轉(zhuǎn)換的邏輯,這樣避免直接調(diào)用get/set屬性方法
public class Workitem{
private int state = WorkitemInfo.PREPARE;
/**
* 委派工作項(xiàng)
*/
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()));
}
/**
* 完成工作項(xiàng)
*/
public void complete() {
if (state != WorkitemInfo.SIGNINED)
throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
setState(WorkitemInfo.COMPLETE);
setCompleted(new Timestamp(System.currentTimeMillis()));
}
}
接下來的做法:
三個(gè)類不變,將WorkitemManager打平,將邏輯移動(dòng)到Workitem
public class WorkitemManager {
private WorkItemDAO workItemDAO;
public void setWorkItemDAO(WorkItemDAO workItemDAO) {
this.workItemDAO = workItemDAO;
}
/**
* 提交工作項(xiàng)
* @param workitemId 工作項(xiàng)ID
*/
public void commitWorkitem(String workitemId){
WorkItem workitem = workItemDAO.getWorkItem(workitemId);
//當(dāng)前工作項(xiàng)提交
workitem.commit();
}
}
實(shí)際上此時(shí)WorkitemManager的功能非常有限,僅僅是事務(wù)邊界和獲取workitem對象,甚至在一些情況下可以省略。
通過一個(gè)Container類將spring的applicationContext進(jìn)行封裝,然后通過getBean()的靜態(tài)方法即可訪問被spring所管理的bean。實(shí)際是將workItemDAO隱式注入了Workitem。
public class Workitem{
/**
* 提交工作項(xiàng)
*/
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");
//查找是否存在下一工作項(xiàng)
WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
//如果不存在則觸發(fā)節(jié)點(diǎn)流轉(zhuǎn)
if (sequenceWorkitem == null) {
instActivity.signal();
}
//否則把下一工作項(xiàng)激活
else {
sequenceWorkitem.setExecutive();
}
}
}
這樣帶來的好處是業(yè)務(wù)邏輯全部被封裝到Domain Model,Domain Model之間的交互變得非常的簡單,沒有頻繁的set/get,直接調(diào)用有業(yè)務(wù)語義的Domain Model的方法即可。問題在于單元測試時(shí)脫離不了spring的容器,workItemDAO需要stub。我覺得這個(gè)問題不大,問題是Domain Model開始變得臃腫,在業(yè)務(wù)邏輯復(fù)雜時(shí)代碼行急劇膨脹。
現(xiàn)在的做法
以上三個(gè)類保持不變,增加一個(gè)類WorkitemExecutor,將業(yè)務(wù)邏輯移步。
public class Workitem{
/**
* 提交工作項(xiàng)
*/
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;
}
/**
* 提交工作項(xiàng)
* @param workitemId 工作項(xiàng)ID
*/
public void commitWorkitem(Workitem workitem){
int sID = workitem.getSequenceId();
//找到所對應(yīng)的節(jié)點(diǎn)
InstActivity instActivity=workitem.getInstActivity();
//查找是否存在下一工作項(xiàng)
WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
//如果不存在則觸發(fā)節(jié)點(diǎn)流轉(zhuǎn)
if (sequenceWorkitem == null) {
instActivity.signal();
}
//否則把下一工作項(xiàng)激活
else {
sequenceWorkitem.setExecutive();
}
}
}
將業(yè)務(wù)邏輯拆分成兩部分,一部分在Workitem,另一部分委托給WorkitemExecutor。實(shí)際上是Domain Model將復(fù)雜邏輯的情況重新外包出去。調(diào)用的時(shí)候,面向的接口還是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。實(shí)際可以這樣認(rèn)為,傳統(tǒng)的方式
Client->(Business Facade)->service(Business Logic 部分依賴Domain Model)->Data Access(DAO)。
現(xiàn)在的方式
Client->(Business Facade)->Domain Model->service->Data Access(DAO)。
另外,在返回client端的查詢的時(shí)候還是傾向于直接調(diào)用DAO,而不是通過Domain Model。
改進(jìn):
注意到代碼中有這么一行
WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO");
確實(shí)是一個(gè)bad smell.當(dāng)代碼中大量出現(xiàn)后,這種造型是很恐怖的。所以采取了一種處理方式:給所有Domain Model繼承一個(gè)父類,在父類里集中管理所有Domain Model所依賴的services,在父類里進(jìn)行造型。
http://m.tkk7.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請注明出處:)
posted on 2008-07-03 18:23
ronghao 閱讀(2617)
評論(2) 編輯 收藏 所屬分類:
工作日志