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

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

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

    Johnny's Collections

    生活總是有太多的無(wú)奈與失望,讓我們以在努力學(xué)習(xí)和工作中獲得的成就感和快樂(lè)來(lái)沖淡它們。

    BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
      10 Posts :: 0 Stories :: 80 Comments :: 0 Trackbacks
    今天一個(gè)曾經(jīng)共事的同行問(wèn)我:“要從編碼轉(zhuǎn)為設(shè)計(jì),大概需要多長(zhǎng)時(shí)間?”
    我的回答是:“編碼本身就是一種設(shè)計(jì),你可以設(shè)計(jì)你的代碼。”

    其實(shí)正如概要設(shè)計(jì)與詳細(xì)設(shè)計(jì),系統(tǒng)設(shè)計(jì)與架構(gòu)設(shè)計(jì)一樣,編碼與設(shè)計(jì)也是沒(méi)有明顯的邊界,每個(gè)正確成長(zhǎng)的程序員,都必須從編碼開(kāi)始,慢慢鍛煉抽象思維、邏輯思維、面向?qū)ο笏季S,然后慢慢的過(guò)渡到系統(tǒng)設(shè)計(jì),再隨著經(jīng)驗(yàn)和知識(shí)的積累,慢慢過(guò)渡到架構(gòu)設(shè)計(jì)。下面我將會(huì)以最近的一個(gè)手頭的編碼任務(wù),簡(jiǎn)單介紹一下如何“設(shè)計(jì)”你的代碼。

    任務(wù)是這樣的,某銀行支付系統(tǒng)的客戶(hù)端接收銀行用戶(hù)錄入的轉(zhuǎn)賬數(shù)據(jù),當(dāng)轉(zhuǎn)賬數(shù)據(jù)被審批通過(guò)后,狀態(tài)轉(zhuǎn)變?yōu)?#8220;transfer”,同時(shí),該客戶(hù)端需要通過(guò)JMS以異步的方式向支付系統(tǒng)后臺(tái)發(fā)送一條帶有轉(zhuǎn)賬記錄(Instruction)的消息,后端在接收到信息之后,需要根據(jù)Instruction的一些相關(guān)信息,首先確定這筆轉(zhuǎn)賬數(shù)據(jù)是直接發(fā)送給真正進(jìn)行轉(zhuǎn)賬的清算(Clearing)銀行系統(tǒng),還是停留在后端系統(tǒng),等待后端系統(tǒng)中需要執(zhí)行的工作流程(work flow)。而后端系統(tǒng)需要對(duì)Instruction執(zhí)行的工作流程有兩個(gè),同時(shí)需要根據(jù)Instruction的一些相關(guān)信息進(jìn)行選擇。
    為了簡(jiǎn)化復(fù)雜度,我這里假設(shè)系統(tǒng)有一個(gè)InstructionHandleMsgDrivenBean,該bean有一個(gè)onMessage()方法,所有業(yè)務(wù)邏輯需要在該方法中實(shí)現(xiàn)。

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

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

    這種做法是最為開(kāi)發(fā)人員歡迎的,因?yàn)樗?jiǎn)單、直接,但這種做法也恰恰反映了開(kāi)發(fā)人員的通病——使用Java編寫(xiě)純面向過(guò)程的代碼。

    好了,說(shuō)了一大堆,如何“設(shè)計(jì)”你的代碼呢?答案是:使用面向?qū)ο笏季S:

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

    有了這個(gè)前提,我可以設(shè)計(jì)出兩個(gè)職責(zé)單一的對(duì)象了:

    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";
        }
    }

    可以看到,我先按職責(zé)劃分了類(lèi),再按職責(zé)抽取了私有方法,“框架”設(shè)計(jì)好 ,為了讓編譯通過(guò),我上面完整的填寫(xiě)了代碼的,然后加上TODO標(biāo)識(shí),然后,我可以編寫(xiě)我的onMessage方法了:

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

    到目前為止,我已經(jīng)用純面向?qū)ο蟮乃季S方式“設(shè)計(jì)”好我的代碼了,這時(shí),我思維非常清晰,因而代碼結(jié)構(gòu)也非常清晰,職責(zé)單一,內(nèi)聚高,耦合低,最后,我可以根據(jù)需求文檔的細(xì)節(jié)(沒(méi)有描述)慢慢的編寫(xiě)我的實(shí)現(xiàn)了。

    復(fù)雜的事物總是由一些較簡(jiǎn)單的事物組成,而這些較簡(jiǎn)單的事物也是由更簡(jiǎn)單的事物組成,如此類(lèi)推。因此,在編寫(xiě)代碼的時(shí)候,先用面向?qū)ο蟮乃季S把復(fù)雜的問(wèn)題分解,再進(jìn)一步分解,最后把簡(jiǎn)單的問(wèn)題各個(gè)擊破,這就是一種設(shè)計(jì)。開(kāi)發(fā)人員只要養(yǎng)成這種習(xí)慣,即使你每天都只是做最底層的編碼工作,其實(shí)你已經(jīng)在參與設(shè)計(jì)工作了,隨著知識(shí)和經(jīng)驗(yàn)的累積,慢慢的,你從設(shè)計(jì)代碼開(kāi)始,上升為設(shè)計(jì)類(lèi)、方法,進(jìn)而是設(shè)計(jì)模塊,進(jìn)而設(shè)計(jì)子系統(tǒng),進(jìn)而設(shè)計(jì)系統(tǒng)……,最終,一步一步成為一個(gè)優(yōu)秀的架構(gòu)師。

    最后,有一個(gè)真理奉獻(xiàn)給浮躁的程序員:

    優(yōu)秀的架構(gòu)師、設(shè)計(jì)師,必定是優(yōu)秀的程序員,不要因?yàn)槟愕穆毼簧仙?,就放棄編碼。

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

    posted on 2010-04-28 00:51 Johnny.Liang 閱讀(4954) 評(píng)論(8)  編輯  收藏 所屬分類(lèi): 編程技巧系統(tǒng)設(shè)計(jì)

    Feedback

    # re: “設(shè)計(jì)”你的代碼 2010-04-28 10:41 zcl
    很有啟發(fā),希望樓主能多寫(xiě)這方面的文章,贊一個(gè)!  回復(fù)  更多評(píng)論
      

    # re: “設(shè)計(jì)”你的代碼 2010-04-28 11:50 grass
    太精辟了。前幾天我還在網(wǎng)上發(fā)貼,被人罵的一踏糊涂,原來(lái)答案在樓主這里。  回復(fù)  更多評(píng)論
      

    # re: “設(shè)計(jì)”你的代碼 2010-04-28 15:02 樂(lè)蜂網(wǎng)美麗俏佳人
    是空間上看簡(jiǎn)單的  回復(fù)  更多評(píng)論
      

    # re: “設(shè)計(jì)”你的代碼[未登錄](méi) 2010-04-29 00:42 onkyo
    小小的砸一下磚,大家探討一下:

    我比較質(zhì)疑以下這兩點(diǎn)
    “純面向?qū)ο蟮乃季S方式” 和 “內(nèi)聚高,耦合低”。

    和原來(lái)的代碼比較的話(huà)就是把原來(lái)集中在一起的代碼分散了。

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

    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(...) {}

    不能繼承,不能重用。

    其次代碼是高耦合的, 當(dāng)流程的判斷條件變更的話(huà)是需要修改代碼的,因?yàn)榕袛鄺l件是寫(xiě)死在代碼里面的。 (當(dāng)然這就是為什么需要工作流框架的原因)  回復(fù)  更多評(píng)論
      

    # re: “設(shè)計(jì)”你的代碼[未登錄](méi) 2010-04-29 00:54 onkyo
    個(gè)人覺(jué)得比較好的方案是聲明一個(gè)interface

    public interface WorkflowFactroy {
    Workflow create(Instruction info)
    }

    把邏輯寫(xiě)在WorkflowFactoryImpl里面, 用ioc注入WorkflowFactoryImpl.  回復(fù)  更多評(píng)論
      

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

    總之,謝謝你的發(fā)言,我只能強(qiáng)調(diào),上面的代碼純屬表達(dá)一種思維方式,況且,不考慮現(xiàn)實(shí)環(huán)境和實(shí)際需求,孤立的去討論一個(gè)類(lèi)是否有接口,一個(gè)方法是否需求繼承,一個(gè)靜態(tài)方法是否必須設(shè)計(jì)為對(duì)象方法,都是沒(méi)有實(shí)際意義的,搞不好就是一種“過(guò)度設(shè)計(jì)”。  回復(fù)  更多評(píng)論
      

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

    首先我非常同意 設(shè)計(jì)是要針對(duì)需求的 的這句話(huà), 這個(gè)流程相當(dāng)穩(wěn)定,不存在多態(tài)的情況,那么第一種寫(xiě)法

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

    我覺(jué)的完全可行。 何必再拆分出兩個(gè)類(lèi)? 還便于閱讀,便于修改。 因?yàn)檫壿嫸技性谝黄鹆恕?這就是面對(duì)過(guò)程的設(shè)計(jì), 非常的合理。

    正如博文題目設(shè)計(jì)你的代碼: 每個(gè)正確成長(zhǎng)的程序員,都必須從編碼開(kāi)始,慢慢鍛煉抽象思維、邏輯思維、面向?qū)ο笏季S,然后慢慢的過(guò)渡到系統(tǒng)設(shè)計(jì),再隨著經(jīng)驗(yàn)和知識(shí)的積累,慢慢過(guò)渡到架構(gòu)設(shè)計(jì)。

    既然我們要抽象上述的代碼, 要使用面對(duì)對(duì)象思維,要重構(gòu)上面的代碼, 就應(yīng)該搞清楚為什么要用抽象,為什么要面對(duì)對(duì)象思維。 抽象和面對(duì)對(duì)象編程的目的無(wú)非是最大限度的重用。 那么就應(yīng)該面對(duì)接口編程, 解耦關(guān)系。

    我的觀(guān)點(diǎn)就是既然要設(shè)計(jì),就要好好設(shè)計(jì)。 如果要用省事的方法,那就用最省事的方法。

      回復(fù)  更多評(píng)論
      

    # re: “設(shè)計(jì)”你的代碼 2010-04-30 17:50 Johnny.Liang
    @onkyo
    明白你的意思,謝謝你的意見(jiàn),我往后會(huì)發(fā)表一些針對(duì)如何使用面向?qū)ο笏季S進(jìn)行設(shè)計(jì),及其真正的好處的博文,當(dāng)中就會(huì)詳細(xì)的說(shuō)明使用面向?qū)ο笏季S在某些場(chǎng)景中的好處。  回復(fù)  更多評(píng)論
      


    只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 猫咪社区免费资源在线观看| 亚洲毛片基地日韩毛片基地| 在线亚洲v日韩v| 国产成人A在线观看视频免费| 亚洲综合在线视频| 永久免费A∨片在线观看| 久久久青草青青国产亚洲免观| 美女露隐私全部免费直播| 麻豆国产VA免费精品高清在线| 亚洲国产精品美女| 曰批全过程免费视频播放网站| 亚洲av无码乱码国产精品fc2 | 国产妇乱子伦视频免费| 亚洲男人天堂2017| 免费无码成人AV在线播放不卡| 亚洲AV永久青草无码精品| 三根一起会坏掉的好痛免费三级全黄的视频在线观看 | 色欲aⅴ亚洲情无码AV蜜桃| 四虎影院免费在线播放| 国产人成亚洲第一网站在线播放| 免费观看成人毛片a片2008| 亚洲娇小性xxxx| 啦啦啦中文在线观看电视剧免费版 | 免费成人午夜视频| 小说区亚洲自拍另类| 韩国欧洲一级毛片免费| 亚洲精品国产av成拍色拍| 日韩一区二区在线免费观看| 亚洲日韩亚洲另类激情文学| 日本免费人成视频播放| 狼人大香伊蕉国产WWW亚洲| 青青青国产色视频在线观看国产亚洲欧洲国产综合 | 美女视频黄的全免费视频网站| 亚洲免费网站在线观看| 曰批全过程免费视频在线观看| 亚洲天堂免费在线| 在线精品免费视频无码的| 精品在线视频免费| 亚洲人成影院在线无码观看| 久久久久久毛片免费看| 久久久久久亚洲精品|