Johnny's Collections

          生活總是有太多的無奈與失望,讓我們以在努力學習和工作中獲得的成就感和快樂來沖淡它們。

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            10 Posts :: 0 Stories :: 80 Comments :: 0 Trackbacks
          今天一個曾經共事的同行問我:“要從編碼轉為設計,大概需要多長時間?”
          我的回答是:“編碼本身就是一種設計,你可以設計你的代碼。”

          其實正如概要設計與詳細設計,系統設計與架構設計一樣,編碼與設計也是沒有明顯的邊界,每個正確成長的程序員,都必須從編碼開始,慢慢鍛煉抽象思維、邏輯思維、面向對象思維,然后慢慢的過渡到系統設計,再隨著經驗和知識的積累,慢慢過渡到架構設計。下面我將會以最近的一個手頭的編碼任務,簡單介紹一下如何“設計”你的代碼。

          任務是這樣的,某銀行支付系統的客戶端接收銀行用戶錄入的轉賬數據,當轉賬數據被審批通過后,狀態轉變為“transfer”,同時,該客戶端需要通過JMS以異步的方式向支付系統后臺發送一條帶有轉賬記錄(Instruction)的消息,后端在接收到信息之后,需要根據Instruction的一些相關信息,首先確定這筆轉賬數據是直接發送給真正進行轉賬的清算(Clearing)銀行系統,還是停留在后端系統,等待后端系統中需要執行的工作流程(work flow)。而后端系統需要對Instruction執行的工作流程有兩個,同時需要根據Instruction的一些相關信息進行選擇。
          為了簡化復雜度,我這里假設系統有一個InstructionHandleMsgDrivenBean,該bean有一個onMessage()方法,所有業務邏輯需要在該方法中實現。

          同時解釋一下詳細的業務細節:
          • 判斷Instruction是否需要停留在后端等待執行指定的工作流程有三個條件:xx、yy、zz,當三個條件都為true時,停留。
          • 判斷Instruction需要走A流程還是B流程,由4個因素的組合確定,如果用“Y”代表true,“N”代表false,那么由這個四個因素組成的“XXXX”一共有16種組合,不同的組合分別走A和B流程,如:YYNN、YYNY to A,NNYY、NNNY to B,……不累贅。
          好了,對于一個純編程人員來說,拿到這樣的需求,感覺邏輯很簡單,可以直接編碼了,于是,他開始一行一行的編寫代碼(偽代碼):

          public void onMessage(InstructionInfo instructionInfo) {
              if(xx && yy && zz) { // 停留在后端等待執行指定的工作流程
                  // 根據每種組合進行條件判斷,走哪個流程
                  if(a==true && b==true && c==true && d==true {
                      ...
                  }
                  else if(...) {...}
                  else if(...) {...}
                  ...
                  else(...) {...}    
              }
          }

          這種做法是最為開發人員歡迎的,因為它簡單、直接,但這種做法也恰恰反映了開發人員的通病——使用Java編寫純面向過程的代碼。

          好了,說了一大堆,如何“設計”你的代碼呢?答案是:使用面向對象思維:

          我們拿到需求之后,可以分析,這個需求大體上分為兩部分:
          • 判斷是否需要停留在后端等待執行指定的工作流程的部分
          • 選擇走哪個工作流程的部分

          有了這個前提,我可以設計出兩個職責單一的對象了:

          public class InstructionHandleDecisionMaker {
              public static boolean isHandledByBackEnd(InstructionInfo info) {
                  return (isXX(...) && isYY(...) && isZZ(...));
              }

              private booolean isXX(...) {
                  //TODO Implement the logic
                  return false;
              }
              private booolean isYY(...) {
                  //TODO Implement the logic
                  return false;
              }
              private booolean isZZ(...) {
                  //TODO Implement the logic
                  return false;
              }
          }

          public class InstructionWorkFlowSelector {
              private static Map mapping = new HashMap();
              static {
                  mapping.input("YYNN",WorkFlow.A);
                  mapping.input("NNYY",WorkFlow.B);
                  ...
              }

              public static WorkFlow getWorkFlow(Instruction info) {
                  StringBuilder result = new StringBuilder();
                  result.append(isA(...)).append(isB(...));
                  result.append(isC(...)).append(isD(...));
                  return mapping.get(result.toString());
              }
              private static String isA(...) {
                  //TODO Implment the logic
                  return "N";
              }
              private static String isB(...) {
                  //TODO Implment the logic
                  return "N";
              }
              private static String isC(...) {
                  //TODO Implment the logic
                  return "N";
              }
              private static String isD(...) {
                  //TODO Implment the logic
                  return "N";
              }
          }

          可以看到,我先按職責劃分了類,再按職責抽取了私有方法,“框架”設計好 ,為了讓編譯通過,我上面完整的填寫了代碼的,然后加上TODO標識,然后,我可以編寫我的onMessage方法了:

          public void onMessage(InstructionInfo instructionInfo) {
              if( InstructionHandleDecisionMaker.isHandledByBackEnd(...) ) {
                  WorkFlow wf =InstructionWorkFlowSelector.getWorkFlow(...);
                  //TODO Implment the logic
              }
          }

          到目前為止,我已經用純面向對象的思維方式“設計”好我的代碼了,這時,我思維非常清晰,因而代碼結構也非常清晰,職責單一,內聚高,耦合低,最后,我可以根據需求文檔的細節(沒有描述)慢慢的編寫我的實現了。

          復雜的事物總是由一些較簡單的事物組成,而這些較簡單的事物也是由更簡單的事物組成,如此類推。因此,在編寫代碼的時候,先用面向對象的思維把復雜的問題分解,再進一步分解,最后把簡單的問題各個擊破,這就是一種設計。開發人員只要養成這種習慣,即使你每天都只是做最底層的編碼工作,其實你已經在參與設計工作了,隨著知識和經驗的累積,慢慢的,你從設計代碼開始,上升為設計類、方法,進而是設計模塊,進而設計子系統,進而設計系統……,最終,一步一步成為一個優秀的架構師。

          最后,有一個真理奉獻給浮躁的程序員:

          優秀的架構師、設計師,必定是優秀的程序員,不要因為你的職位上升了,就放棄編碼。

          補充說明:本博文純粹是討論一種思維習慣,不要把其做法生搬硬套,不管實際情況,直接在編碼的時候這樣做,不見得是最好的選擇。在實際編碼中,有如下問題你必須考慮:
          • 你需要考慮業務邏輯的可重用性和復雜程度,是否有必要設計出新的類或抽取新的私有方法來封裝邏輯,或者直接在原方法上編碼(如果足夠簡單)。
          • 新的業務邏輯,是否在某些地方已經存在,可以復用,即使不存在,這些邏輯是應該封裝到新的類中,還是應該放置到現有的類中,這需要進行清晰的職責劃分。
          • 需要在設計和性能上作出權衡。
          • 如果在現成的系統中增加新的功能,而現成系統的編碼風格與你想要的相差很遠,但你又沒有足夠的時間成本來進行重構,那么還是應該讓你的代碼與現成系統保持一致的風格。

          posted on 2010-04-28 00:51 Johnny.Liang 閱讀(4963) 評論(8)  編輯  收藏 所屬分類: 編程技巧系統設計

          Feedback

          # re: “設計”你的代碼 2010-04-28 10:41 zcl
          很有啟發,希望樓主能多寫這方面的文章,贊一個!  回復  更多評論
            

          # re: “設計”你的代碼 2010-04-28 11:50 grass
          太精辟了。前幾天我還在網上發貼,被人罵的一踏糊涂,原來答案在樓主這里。  回復  更多評論
            

          # re: “設計”你的代碼 2010-04-28 15:02 樂蜂網美麗俏佳人
          是空間上看簡單的  回復  更多評論
            

          # re: “設計”你的代碼[未登錄] 2010-04-29 00:42 onkyo
          小小的砸一下磚,大家探討一下:

          我比較質疑以下這兩點
          “純面向對象的思維方式” 和 “內聚高,耦合低”。

          和原來的代碼比較的話就是把原來集中在一起的代碼分散了。

          首先 InstructionHandleDecisionMaker 和 InstructionWorkFlowSelector 就不是面對對象的設計, 用的是都是static函數。 實際上就是把原來代碼中的
          onMessage 中的代碼, 歸了一下類,拆成一些小函數, 然后再插到InstructionHandleDecisionMaker 和 InstructionWorkFlowSelector 文件中去。 其實際上就是

          public void onMessage(InstructionInfo instructionInfo) {
          if(isHandledByBackEnd(...) ) {
          WorkFlow wf =getWorkFlow(...);
          //TODO Implment the logic
          }
          }

          private static Map mapping = new HashMap();
          static {
          mapping.input("YYNN",WorkFlow.A);
          mapping.input("NNYY",WorkFlow.B);
          ...
          }

          private WorkFlow getWorkFlow(Instruction info) {
          ...
          }

          private String isA(...) {}
          private String isB(...) {}
          private String isC(...) {}
          private String isD(...) {}

          不能繼承,不能重用。

          其次代碼是高耦合的, 當流程的判斷條件變更的話是需要修改代碼的,因為判斷條件是寫死在代碼里面的。 (當然這就是為什么需要工作流框架的原因)  回復  更多評論
            

          # re: “設計”你的代碼[未登錄] 2010-04-29 00:54 onkyo
          個人覺得比較好的方案是聲明一個interface

          public interface WorkflowFactroy {
          Workflow create(Instruction info)
          }

          把邏輯寫在WorkflowFactoryImpl里面, 用ioc注入WorkflowFactoryImpl.  回復  更多評論
            

          # re: “設計”你的代碼 2010-04-29 09:40 Johnny.Liang
          @onkyo
          呵呵,回復一下這為同學的兩個評論,首先,你說得對,static就不是面向對象,純面向對象是沒有static函數的,但我要解釋兩點,上面的代碼純屬演示如何改變一種思維方式,我并沒有過于斟酌于代碼的細節,如果要純面向對象的話,我可以把static聲明為對象方法,然后讓這個類變成Singleton;其次,如果所有東西都要考慮繼承的話,就是過度設計對了,正如我在本博文的最后的特別說明,設計是要針對需求的,假如我這個流程相當穩定,不存在多態的情況,那么我就(至少在目前)不需要過度的把它設計為接口,然后再提供實現類,再通過依賴注入,而關于你提到的private方法不能繼承和重用,這也是一個好問題,假如根據實際情況,我不希望我的類或方法被繼承或重寫,我就需要聲明其為final/private了,君不見JDK的很多類都是final的嗎?這同樣也回答了你第二個評論的問題,沒需要多態,或沒需求切換實現,就沒必要接口。

          總之,謝謝你的發言,我只能強調,上面的代碼純屬表達一種思維方式,況且,不考慮現實環境和實際需求,孤立的去討論一個類是否有接口,一個方法是否需求繼承,一個靜態方法是否必須設計為對象方法,都是沒有實際意義的,搞不好就是一種“過度設計”。  回復  更多評論
            

          # re: “設計”你的代碼[未登錄] 2010-04-30 16:39 onkyo
          @Johnny.Liang
          我只是就博文中我不太認同的地方發表一下看法,大家探討一下相互提高。

          首先我非常同意 設計是要針對需求的 的這句話, 這個流程相當穩定,不存在多態的情況,那么第一種寫法

          public void onMessage(InstructionInfo instructionInfo) {
          if(xx && yy && zz) { // 停留在后端等待執行指定的工作流程
          // 根據每種組合進行條件判斷,走哪個流程
          if(a==true && b==true && c==true && d==true {
          ...
          }
          else if(...) {...}
          else if(...) {...}
          ...
          else(...) {...}
          }
          }

          我覺的完全可行。 何必再拆分出兩個類? 還便于閱讀,便于修改。 因為邏輯都集中在一起了。 這就是面對過程的設計, 非常的合理。

          正如博文題目設計你的代碼: 每個正確成長的程序員,都必須從編碼開始,慢慢鍛煉抽象思維、邏輯思維、面向對象思維,然后慢慢的過渡到系統設計,再隨著經驗和知識的積累,慢慢過渡到架構設計。

          既然我們要抽象上述的代碼, 要使用面對對象思維,要重構上面的代碼, 就應該搞清楚為什么要用抽象,為什么要面對對象思維。 抽象和面對對象編程的目的無非是最大限度的重用。 那么就應該面對接口編程, 解耦關系。

          我的觀點就是既然要設計,就要好好設計。 如果要用省事的方法,那就用最省事的方法。

            回復  更多評論
            

          # re: “設計”你的代碼 2010-04-30 17:50 Johnny.Liang
          @onkyo
          明白你的意思,謝謝你的意見,我往后會發表一些針對如何使用面向對象思維進行設計,及其真正的好處的博文,當中就會詳細的說明使用面向對象思維在某些場景中的好處。  回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 西畴县| 洪洞县| 阿克苏市| 邮箱| 中超| 霍山县| 玛沁县| 太原市| 清流县| 常宁市| 绥江县| 华坪县| 乐业县| 洛隆县| 隆昌县| 府谷县| 盐亭县| 米易县| 临武县| 泰兴市| 阜宁县| 富锦市| 托克托县| 黔东| 湘乡市| 古交市| 长宁县| 赤城县| 武胜县| 法库县| 渝中区| 多伦县| 定远县| 子长县| 乌拉特中旗| 拜城县| 涞水县| 吕梁市| 含山县| 灌南县| 漯河市|