<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    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 閱讀(4953) 評論(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
    明白你的意思,謝謝你的意見,我往后會發表一些針對如何使用面向對象思維進行設計,及其真正的好處的博文,當中就會詳細的說明使用面向對象思維在某些場景中的好處。  回復  更多評論
      


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


    網站導航:
     
    主站蜘蛛池模板: 免费人成视频在线观看不卡| 亚洲人成影院在线无码按摩店| 美女裸体无遮挡免费视频网站| 亚洲AV无码成人专区片在线观看 | 在线视频精品免费| 亚洲国产精品18久久久久久| 中文字幕不卡亚洲| 国产精品久久永久免费| 一级做a爰片性色毛片免费网站| 久久精品国产99精品国产亚洲性色| 欧美a级成人网站免费| 少妇亚洲免费精品| 亚洲国产成人精品久久 | 亚洲国产成人精品青青草原| 亚洲裸男gv网站| 100000免费啪啪18免进| selaoban在线视频免费精品| jlzzjlzz亚洲jzjzjz| 亚洲另类激情综合偷自拍图| 好吊妞在线成人免费| 无码精品一区二区三区免费视频| 亚洲av无码偷拍在线观看| 337p欧洲亚洲大胆艺术| 亚洲伦乱亚洲h视频| 卡1卡2卡3卡4卡5免费视频| 叮咚影视在线观看免费完整版| 亚洲国产美女精品久久久| 91情国产l精品国产亚洲区| 亚洲欧洲一区二区三区| 在线a人片天堂免费观看高清| 日本免费电影一区二区| 一级毛片人与动免费观看| 亚洲女女女同性video| 亚洲首页在线观看| 欧洲亚洲国产清在高| www.亚洲一区| 国产精品国产自线拍免费软件| 永久黄色免费网站| 国产成人久久AV免费| 91国内免费在线视频| 一级特级aaaa毛片免费观看|