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

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

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

    OOPAA

    Focusing on OO, Patterns, Architecture, and Agile
    posts - 29, comments - 75, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    關(guān)于重構(gòu)

    重構(gòu)是一種改善已有代碼和設(shè)計(jì)的有效手段,Martin Fowler的著作Refactoring:Improving the Design of Existing Code一書里提出了若干種重構(gòu)的模式,深刻地影響了眾多的開發(fā)人員。但如果認(rèn)為重構(gòu)只能做到小范圍的代碼優(yōu)化,或者設(shè)計(jì)優(yōu)化,并視之為無法影響更高層面工作的雕蟲小技,那就大錯(cuò)特錯(cuò)了。之后 Joshua Kerievsky 的著作 Refactoring to Patterns則創(chuàng)造性地把重構(gòu)和模式聯(lián)系在一起,讓人們認(rèn)識(shí)到重構(gòu)的巨大威力。重構(gòu)從來不是程序員躲在象牙塔孤芳自賞的技術(shù),也可以對(duì)系統(tǒng)的設(shè)計(jì)開發(fā)發(fā)揮巨大的作用。

    如果說Martin的Refactoring只是深刻地影響了普通開發(fā)人員的程序設(shè)計(jì)和代碼編寫,Joshua的Refactoring to Patterns則切切實(shí)實(shí)地給架構(gòu)設(shè)計(jì)人員或者tech lead的工作指出了革命性的變化。那么,對(duì)于項(xiàng)目的功能開發(fā),重構(gòu)又意味著什么呢?對(duì)于項(xiàng)目經(jīng)理來講,應(yīng)用重構(gòu)的技術(shù)和思想,對(duì)整個(gè)項(xiàng)目的功能開發(fā)是否能帶來特別的好處?下面將通過一個(gè)例子給大家展示在開發(fā)新功能時(shí),對(duì)開發(fā)的每一步都保持重構(gòu)的思想,將整體功能的開發(fā)分解成若干步驟的“重構(gòu)”,從而非常簡(jiǎn)易清晰地完成功能開發(fā)。

    新功能的提出

    某系統(tǒng)中存在 ReferenceFile 類,作為用戶向系統(tǒng)上傳的文件的抽象,是系統(tǒng)其他地方會(huì)使用到的參考文檔。在最初的需求中,參考文檔與用戶上傳的文件一一對(duì)應(yīng),并且用戶能指定某些系統(tǒng)特定的屬性,比如文檔類別、文檔管理部門等。于是在最初的設(shè)計(jì)中,該類的屬性包含兩部分:一部分是前面提到的系統(tǒng)屬性;另一部分是描述文件信息的屬性,比如文件名、文件存儲(chǔ)路徑等。請(qǐng)注意,這里我將“參考文檔”與“上傳文件”兩個(gè)概念區(qū)分出來,是為了便于下文解釋??偟膩碚f,在這個(gè)階段,“參考文檔”就是系統(tǒng)對(duì)“上傳文件”的抽象。

    接到這個(gè)需求之后,我們使用TDD,很快就驅(qū)動(dòng)設(shè)計(jì)出該類的業(yè)務(wù)方法;再使用Acceptance TDD,又對(duì)該類的功能的進(jìn)行了全面的覆蓋;最后使用hibernate的O/R Mapping,按照屬性和表字段一對(duì)一的關(guān)系,把該類和數(shù)據(jù)庫的表關(guān)聯(lián)起來。完成UI方面的設(shè)計(jì),并把前后臺(tái)整合在一起。系統(tǒng)上線試運(yùn)行后用戶認(rèn)為這塊很好地契合了需求。

    但是,需求總是不斷在變的。上線過程中,用戶的上級(jí)部門提出參考文檔應(yīng)該可以對(duì)應(yīng)到多個(gè)上傳文件,系統(tǒng)其他地方使用時(shí)把其下所有上傳的文件作為一個(gè)“參考文檔”整體來對(duì)待。也就是說,對(duì) ReferenceFile 類而言,其中的系統(tǒng)屬性仍然是保持一份,但是上傳文件的屬性則變成多份。概括下來,客戶提出的新需求如下:

       1. 參考文檔可以管理多個(gè)上傳文件
       2. 用戶創(chuàng)建或者修改參考文檔時(shí),可以同時(shí)上傳多個(gè)文件,并能對(duì)已上傳的文件進(jìn)行刪除修改
       3. 系統(tǒng)在其他地方仍然是針對(duì)參考文檔來參考引用用戶上傳的文件
       4. 參考文檔的預(yù)覽和展示需要調(diào)整成支持多個(gè)上傳文件

    實(shí)現(xiàn)過程

    該系統(tǒng)是標(biāo)準(zhǔn)的j2ee web 分層系統(tǒng),包括web UI、controller、service、domain model、dao這幾層。本文的重點(diǎn)是如何應(yīng)用重構(gòu)開發(fā)功能,本文將著重關(guān)注于domain層的改動(dòng),會(huì)包括domain model API的改動(dòng),以及domain model 持久化機(jī)制的改動(dòng)。其他層次,比如controller、service等,因?yàn)橹饕亲鳛閐omain model的消費(fèi)者,主要是使用domain model 的public API,故放在一起作為整體來對(duì)待,下文將統(tǒng)一稱為client 代碼。至于最外層的 web UI層,則因?yàn)橹饕歉鶕?jù)系統(tǒng)功能提供交互上的操作和內(nèi)容展現(xiàn),而且大部分情況下也會(huì)有專門UI設(shè)計(jì)開發(fā),本文就不涉及了。

    另外,系統(tǒng)還包括大量不同層次的測(cè)試代碼,比如unit test、functional test、integration test和regression test等等。從另外一個(gè)角度,測(cè)試代碼又可以分成2部分:text fixture和test case。test fixture主要是負(fù)責(zé)測(cè)試數(shù)據(jù)的準(zhǔn)備,test case才是測(cè)試用例的實(shí)現(xiàn)代碼。前面提到的測(cè)試,除了unit test之外都主要是基于 web UI 模擬用戶使用系統(tǒng)功能,test case 主要是針對(duì) web UI 來寫,故對(duì)于這部分的測(cè)試而言,domain model 的修改主要會(huì)影響到測(cè)試數(shù)據(jù)的準(zhǔn)備。而對(duì)于 unit test,又可以根據(jù)SUT的不同,分為幾個(gè)部分:針對(duì)model的unit test、針對(duì)client(包括controller和service)的unit test。其中,針對(duì)model的unit test也只是model API的消費(fèi)者,也可以視為domain model的client。針對(duì)controller和service的unit test,理論上也只針對(duì)于SUT的API,對(duì)model的API依賴也只是在test fixture那塊。所以,根據(jù)我們的分析,我們知道測(cè)試代碼可以簡(jiǎn)化成兩部分,一部分是與controller/service類似的domain model的client,另一部分是使用domain model生成一組aggregation的test fixture。

    綜上所述,我們把整個(gè)功能實(shí)現(xiàn)過程中涉及的工作主要?dú)w類為:domain model API的改動(dòng)、domain model持久化機(jī)制的修改、domain model client的修改,以及test fixture的修改。現(xiàn)在對(duì)于需要做什么事情,就變得清晰了。我們接下來對(duì)前面三項(xiàng)工作來分析。
    面臨的現(xiàn)狀
    仔細(xì)分析我們面臨的情況:

       1. 文件的相關(guān)信息在原始的 ReferenceFile 類里面是作為一對(duì)一的屬性組存在
       2. ReferenceFile 類使用 Hibernate 進(jìn)行屬性字段一對(duì)一的持久化
       3. ReferenceFile 類以及原功能有 unit test、dao test,以及functional test 覆蓋

    此時(shí)的 ReferenceFile 類是這樣的:
    public class ReferenceFile {
        
    private String category;
        
    private String fileName;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }
    ReferenceFile 類的hibernate映射文件是這樣的:
    <class name="ReferenceFile" table="referenceFiles">
        
    <id/>
        
    <property name="category"/>
        
    <property name="fileName"/>
        //
    </class>
    回頭看看在這次功能調(diào)整中,我們需要做哪幾項(xiàng)任務(wù)?其中會(huì)涉及哪些方面?
    •   domain model 的修改
    •   domain model 持久化機(jī)制的修改
    •   domain model 增加一對(duì)多的關(guān)系
    抽取新類
    domain model 的修改
    很明顯,隨著需求的變化,作為一組時(shí)時(shí)刻刻同時(shí)出現(xiàn)而且內(nèi)聚性非常強(qiáng)的屬性,原來記錄文件相關(guān)信息的屬性組,比如文件名、上傳路徑以及類型等等,以及操作這些屬性的方法需要抽取到一個(gè)單獨(dú)的類里面。Martin Fowler 在Refactoring:Improving the Design of Existing Code里面寫到“...consider where it can be split...A good sign is that a subste of the data and a subset of the methods seem to go together.”因此,我們決定把這些屬性組和方法抽取到一個(gè)新類。新的類的職責(zé)變成維護(hù)上傳文件的相關(guān)信息,而 ReferenceFile 則化身為一組上傳文件的集合,不用操心文件的存儲(chǔ)和具體細(xì)節(jié),更利于系統(tǒng)其他地方進(jìn)行引用。

    那我們?cè)撊绾芜M(jìn)行演化呢?這里我們可以使用 Martin Fowler在Refactoring書中的“Extract Class”技巧。請(qǐng)大家自行參閱,就不具體講了。經(jīng)過這一步,我們現(xiàn)在可以得到這樣一個(gè)結(jié)構(gòu):ReferenceFile has an Attachment。 這兩個(gè)類的代碼大概如下:
    public class ReferenceFile {
        
    private String category;
        
    private Attachment attachment;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }

    public
     class Attachnment {
        
    private String fileName;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }
    domain model 持久化機(jī)制的修改
    接下來,我們需要修改 ReferenceFile 的持久化機(jī)制。在原始的設(shè)計(jì)里面,ReferenceFile類的屬性一一對(duì)應(yīng)到數(shù)據(jù)庫表中的字段?,F(xiàn)在屬性被分到了兩個(gè)對(duì)象里面,為了 Hibernate依舊能把這些屬性都持久化到一張數(shù)據(jù)庫表里面,我們使用了 Hibernate 提供的 component配置。下面是改動(dòng)后的配置:
    <class name="ReferenceFile" table="referenceFiles">
     
    <id/>
      <property name="category"/>
      
    <component class="Attachment">
        
    <property name="fileName"/>
        //
      
    </component>
    </class>
    運(yùn)行測(cè)試,OK,所有的測(cè)試都pass了。至此,我們抽取新類的步驟就完成了。接下來,我們需要完成“一對(duì)多”的演化。
    公開新類
    domain model 的修改
    在這里面,我們需要將 ReferenceFile 類里面的 Attachment 類公布出來,直接在client code里面使用這個(gè)類。這樣,原本屬于 Attachment 類的方法就能徹底地從 ReferenceFile 類里面移走,ReferenceFile類只留下必要的業(yè)務(wù)方法和 Attachment 對(duì)象的getter/setter。Martin Fowler在Refactoring:Improving the Design of Existing Code里提到“move methods”,我們采用這種技巧,很容易地把原來與Attachment類相關(guān)的業(yè)務(wù)方法都移到Attachment類里面,ReferenceFile類里面只保留對(duì)attachment屬性的getter/setter方法。公布Attachment對(duì)象之后的結(jié)構(gòu):
    public class ReferenceFile {
        
    private String category;
        
    private Attachment attachment;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }

    public
     class Attachnment {
       
    private Long id;
        
    private String fileName;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }
    domain model 持久化機(jī)制的修改
    這里,我們就考慮把Attachment單獨(dú)持久化到自己的數(shù)據(jù)庫表里面了。原來的component就變成了現(xiàn)在一對(duì)一關(guān)聯(lián)。改動(dòng)后的配置如下:
    <class name="ReferenceFile" table="referenceFiles">
      <
    id/>
      
    <property name="category"/>
      
    <one-to-one name="attachment" class="Attachment"/>
        //
    </class>
    <class name="Attachment" table="attachments">
      
    <id/>
      <property name="fileName"/>
    </class>
    實(shí)現(xiàn)類之間的一對(duì)多聯(lián)系
    domain model 的修改
    到這里,讀者就能發(fā)現(xiàn)這是一種Kent Beck曾經(jīng)總結(jié)過的“First One, Then Many”情況。關(guān)于“First One,Then Many”,Kent Beck曾寫了一篇文章介紹如何可靠地?fù)肀ё兓?,原文鏈接如下http://www.threeriversinstitute.org /FirstOneThenMany.html。在那篇文章中,Kent的問題是面對(duì)未來可能的需求變化,如何使用 Succession 的方式幫助系統(tǒng)架構(gòu)平滑演化。下面是 Kent 的觀點(diǎn):
        Applied to software design, succession refers to creating a design in stages. The first stage is not where you expect to end up, but it provides value. You have to pay the price to progress from stage to stage, but if jumping to the final stage is prohibitively expensive in time or money, or if you don't know enough to design the "final" stage, or if (as postulated above) the customers don't know enough to specify the system that would use the final stage, then succession provides a disciplined, reasonably efficient way forward.

    那么,在本文的功能開發(fā)之中,我們是如何做到的?

    1. 增加字段attachments,以及getter/setter
    2. 修改原來單個(gè)Attachment的getter/setter,改成從attachments里面得到首元素或者往里面添加新元素,如getAttachments().get(0)
    3. 運(yùn)行測(cè)試,確保所有測(cè)試都通過
    4. inline ReferenceFile類里面的對(duì)單個(gè)Attachment的getter/setter方法。這里要注意test fixture里面對(duì)domain model的aggregation的創(chuàng)建,而且因?yàn)樯婕皩?duì)List的操作,所以可能需要修改原來的測(cè)試代碼和test fixture
    5. 運(yùn)行測(cè)試,確保所有測(cè)試都通過

    到這里,“一對(duì)多”的工作完成之后,ReferenceFile 和 Attachment 類就變成了下文的樣子:

    public class ReferenceFile {
        
    private String category;
        
    private List<Attachment> attachments;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }

    public
     class Attachnment {
       
    private Long id;
        
    private String fileName;
        
    //相應(yīng)的 getter/setter,以及業(yè)務(wù)方法
    }
    domain model 持久化機(jī)制的修改
    為了能實(shí)現(xiàn)一對(duì)多的實(shí)體關(guān)系,我們需要引入新的表作為“多”方,并保持“一”方的主鍵。使用Hibernate提供的one-to-many很容易做到這點(diǎn),接下來是簡(jiǎn)單的配置文件:
    <class name="ReferenceFile" table="referenceFiles">
      
    <property name="category"/>
      
    <set name="attachments" cascade="all" >
        
    <key column="id"/>
        
    <one-to-many class="Attachment"/>
      
    </set>
      //
    </class>
    <class name="Attachment" table="attachments">
      
    <property name="fileName"/>
    </class>

    結(jié)論

    至此,我們就完成了新功能的開發(fā),可以看出整個(gè)過程的思路非常明顯,而且因?yàn)橹饕茄刂貥?gòu)的思想一路下來,思路非常清晰。另外,因?yàn)橹貥?gòu)已經(jīng)有成熟的IDE支持,我們可以利用到IDE的很多便利,這從另一方面也給我們帶來了非常的效率。

    從整個(gè)過程來看,重構(gòu)的一些方法和思想,不僅可以讓我們對(duì)遺留代碼進(jìn)行優(yōu)化,使之能有利于新功能的開發(fā)(比如本文中的抽取新類和公開新類,都是為了下文的“由一到多”的功能開發(fā)),而且可以讓我們?cè)陂_發(fā)功能的時(shí)候能從一個(gè)更高的角度來分解功能的開發(fā)工作,從而把原本復(fù)雜無序的過程簡(jiǎn)化抽象成一段明確的重構(gòu)鏈。那么,重構(gòu)是否就是開發(fā)人員開發(fā)軟件的領(lǐng)域?qū)僬Z言呢(refactoring as DSLs to developers' development)?敬請(qǐng)期待本博關(guān)于這點(diǎn)的其他博文。

    評(píng)論

    # re: 一次 Refactoring to Fucktionality 的實(shí)踐  回復(fù)  更多評(píng)論   

    2009-05-16 17:39 by HgG
    Fucktionality?
    Dude, you really need to get laid.

    # re: 一次 Refactoring to Fuctionality 的實(shí)踐  回復(fù)  更多評(píng)論   

    2009-05-16 23:14 by jinfeng_wang
    java composite模式
    http://blog.csdn.net/DL88250/archive/2007/09/22/1796151.aspx

    這個(gè)可能更適合

    # re: 一次 Refactoring to Fuctionality 的實(shí)踐  回復(fù)  更多評(píng)論   

    2009-05-17 18:20 by mingj
    @jinfeng_wang
    hi, 具體的解決方案其實(shí)倒不重要
    這篇文章試圖展現(xiàn)一種開發(fā)方式:
    通過分解功能需求,按照OO思想設(shè)計(jì),使得每個(gè)階段的工作都能是原子性質(zhì)的重構(gòu)

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产一精品一aⅴ一免费| 亚洲精品av无码喷奶水糖心| 白白国产永久免费视频| 黄页免费在线观看 | 99久久久国产精品免费牛牛四川| 亚洲乱码国产乱码精华| 亚洲天堂福利视频| 国产成人无码综合亚洲日韩| vvvv99日韩精品亚洲| 国产精品无码免费播放| 99在线免费观看视频| 成在人线av无码免费高潮喷水| 污污视频免费观看网站| 亚洲色大成网站www永久网站| 亚洲天堂福利视频| 亚洲高清资源在线观看| 内射干少妇亚洲69XXX| 日韩亚洲欧洲在线com91tv| 中文字幕亚洲日韩无线码| 少妇亚洲免费精品| 免费国产美女爽到喷出水来视频| 免费看美女被靠到爽的视频| 野花高清在线观看免费完整版中文 | 人妻无码一区二区三区免费| 成全视频高清免费观看电视剧| 2022免费国产精品福利在线| 一级毛片a免费播放王色电影| 边摸边吃奶边做爽免费视频99 | 亚洲日韩在线观看免费视频| 亚洲av高清在线观看一区二区 | 亚洲最大av资源站无码av网址| 亚洲另类自拍丝袜第1页| 亚洲成人高清在线观看| 亚洲国产电影在线观看| 国产精品高清视亚洲精品| 亚洲日韩国产精品乱-久| 亚洲GV天堂GV无码男同| 亚洲精品无码成人| 国产午夜亚洲精品不卡电影| 青娱乐在线视频免费观看| 一区二区3区免费视频|