posts - 193,  comments - 520,  trackbacks - 0
          關(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();
                  
          //找到所對(duì)應(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對(duì)象,甚至在一些情況下可以省略。

          通過一個(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之間的交互變得非常的簡(jiǎn)單,沒有頻繁的set/get,直接調(diào)用有業(yè)務(wù)語義的Domain Model的方法即可。問題在于單元測(cè)試時(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();
                  
          //找到所對(duì)應(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://www.aygfsteel.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處:)
          posted on 2008-07-03 18:23 ronghao 閱讀(2621) 評(píng)論(2)  編輯  收藏 所屬分類: 工作日志

          FeedBack:
          # re: 從貧血到充血Domain Model
          2011-03-14 09:04 | 人在江湖
          請(qǐng)教,有句沒看懂
          WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO");
          這個(gè)為什么不是注射進(jìn)來的呢?

          覺得你“接下來的做法”和“現(xiàn)在的做法”沒啥區(qū)別啊,呵呵,就是沒有封裝private方法,而是單弄了類。

          張小慶的故事挺好的,不希望故事結(jié)局太灰暗。樓主牛人,給程序員們些念想吧,呵呵。  回復(fù)  更多評(píng)論
            
          # re: 從貧血到充血Domain Model[未登錄]
          2011-03-14 18:33 | ronghao
          @人在江湖
          無法注入,WorkItem是不受容器管理的。
          謝謝關(guān)注張小慶!  回復(fù)  更多評(píng)論
            
          <2011年3月>
          272812345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          關(guān)注工作流和企業(yè)業(yè)務(wù)流程改進(jìn)。現(xiàn)就職于ThoughtWorks。新浪微博:http://weibo.com/ronghao100

          常用鏈接

          留言簿(38)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          常去的網(wǎng)站

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 邵阳县| 平远县| 阜城县| 石楼县| 河北区| 彰化市| 深圳市| 武冈市| 皋兰县| 黎城县| 灵璧县| 宁蒗| 开封县| 石棉县| 崇礼县| 莱州市| 高台县| 丰镇市| 长治县| 五台县| 上犹县| 射阳县| 磐安县| 襄汾县| 靖远县| 昭通市| 汨罗市| 岢岚县| 怀柔区| 阿坝| 阳西县| 定陶县| 元江| 紫金县| 宁河县| 宜兴市| 黔南| 启东市| 泰安市| 汉沽区| 淮南市|