posts - 193,  comments - 520,  trackbacks - 0
          關(guān)于Domain Model的討論已經(jīng)非常多了,炒炒冷飯,這里是自己的一些做法。
          以Workitem(工作流里的工作項)作為例子

          最開始的做法:
          一個實體類叫做Workitem,指的是一個工作項或者稱為任務(wù)項
          一個DAO類叫做WorkitemDao
          一個業(yè)務(wù)邏輯類叫做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);
                      
          //當(dāng)前工作項結(jié)束
                  workitem.complete();
                  
          int sID = workitem.getSequenceId();
                  
          //找到所對應(yīng)的節(jié)點
                  InstActivity instActivity=workitem.getInstActivity();
                  
          //查找是否存在下一工作項
                  WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
                  
          //如果不存在則觸發(fā)節(jié)點流轉(zhuǎn)
                  if (sequenceWorkitem == null) {
                      instActivity.signal();
                  }
                  
          //否則把下一工作項激活
                  else {
                      sequenceWorkitem.setExecutive();
                  }
              }
              
          }


          Workitem類里有一些狀態(tài)轉(zhuǎn)換的邏輯,這樣避免直接調(diào)用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);
                      
          //當(dāng)前工作項提交
                  workitem.commit();
              }
              
          }

          實際上此時WorkitemManager的功能非常有限,僅僅是事務(wù)邊界和獲取workitem對象,甚至在一些情況下可以省略。

          通過一個Container類將spring的applicationContext進行封裝,然后通過getBean()的靜態(tài)方法即可訪問被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);
                  
          //如果不存在則觸發(fā)節(jié)點流轉(zhuǎn)
                  if (sequenceWorkitem == null) {
                      instActivity.signal();
                  }
                  
          //否則把下一工作項激活
                  else {
                      sequenceWorkitem.setExecutive();
                  }
              }

          }


          這樣帶來的好處是業(yè)務(wù)邏輯全部被封裝到Domain Model,Domain Model之間的交互變得非常的簡單,沒有頻繁的set/get,直接調(diào)用有業(yè)務(wù)語義的Domain Model的方法即可。問題在于單元測試時脫離不了spring的容器,workItemDAO需要stub。我覺得這個問題不大,問題是Domain Model開始變得臃腫,在業(yè)務(wù)邏輯復(fù)雜時代碼行急劇膨脹。

          現(xiàn)在的做法
          以上三個類保持不變,增加一個類WorkitemExecutor,將業(yè)務(wù)邏輯移步。

          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();
                  
          //找到所對應(yīng)的節(jié)點
                  InstActivity instActivity=workitem.getInstActivity();
                  
          //查找是否存在下一工作項
                  WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
                  
          //如果不存在則觸發(fā)節(jié)點流轉(zhuǎn)
                  if (sequenceWorkitem == null) {
                      instActivity.signal();
                  }
                  
          //否則把下一工作項激活
                  else {
                      sequenceWorkitem.setExecutive();
                  }
              }
              
          }


          將業(yè)務(wù)邏輯拆分成兩部分,一部分在Workitem,另一部分委托給WorkitemExecutor。實際上是Domain Model將復(fù)雜邏輯的情況重新外包出去。調(diào)用的時候,面向的接口還是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。實際可以這樣認為,傳統(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端的查詢的時候還是傾向于直接調(diào)用DAO,而不是通過Domain Model。

          改進:
          注意到代碼中有這么一行
          WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO");

          確實是一個bad smell.當(dāng)代碼中大量出現(xiàn)后,這種造型是很恐怖的。所以采取了一種處理方式:給所有Domain Model繼承一個父類,在父類里集中管理所有Domain Model所依賴的services,在父類里進行造型。




          http://www.aygfsteel.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請注明出處:)
          posted on 2008-07-03 18:23 ronghao 閱讀(2630) 評論(2)  編輯  收藏 所屬分類: 工作日志

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

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

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

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

          常用鏈接

          留言簿(38)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          常去的網(wǎng)站

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 扶余县| 隆回县| 临海市| 灯塔市| 古丈县| 资阳市| 兴宁市| 奉化市| 攀枝花市| 琼结县| 于都县| 广南县| 南江县| 仪陇县| 宁蒗| 岱山县| 澄迈县| 水富县| 灵台县| 乌鲁木齐县| 扎赉特旗| 祁门县| 孝义市| 晋中市| 蒙城县| 平遥县| 肇州县| 垣曲县| 青铜峡市| 甘泉县| 潜江市| 邵武市| 栾城县| 久治县| 宁津县| 大港区| 宜昌市| 嘉义县| 开鲁县| 永州市| 布尔津县|