1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        程序員必備技能:我覺得必須會重構(gòu),哈哈哈

        共 4332字,需瀏覽 9分鐘

         ·

        2020-12-03 13:14

        點擊上方[全棧開發(fā)者社區(qū)]右上角[...][設(shè)為星標?]

        為什么要重構(gòu)

        你可能正在面對一個遺留系統(tǒng),增加一個需求要改動好幾個文件,定位 Bug 經(jīng)常要花掉一整天時間,修復(fù)一個 Bug 可能又制造了 3 個新的 Bug。

        你也可能會為了軟件設(shè)計和同事爭得面紅耳赤,討論如何應(yīng)對未來可能出現(xiàn)的需求變化。

        為了開發(fā)一個新需求,你打開一份源代碼,完全不知所云嘛,你吐槽著誰能寫出如此不堪入目的代碼,于是決定查看版本記錄,把這個家伙找出來鄙視一下。

        然后你在提交歷史里看到了自己的名字... 恭喜你,你進步了。

        如果你是一個積極進取的程序員,通常在幾個月甚至幾個星期之后就認不出自己寫的代碼。你總能發(fā)現(xiàn)更好的實現(xiàn)方式,讓代碼更加優(yōu)雅。

        隨著增加新特性或需求變更,代碼會變得越來越難以維護。敏捷軟件開發(fā)的十二條原則中有一條是:我們始終擁抱需求變化,哪怕是在軟件開發(fā)的后期。

        為了達到這種狀態(tài),我們就要在開發(fā)過程中持續(xù)地優(yōu)化代碼。

        而重構(gòu)這項技術(shù),為我們提供了一種更可控的方式來優(yōu)化代碼。

        重構(gòu)是什么

        重構(gòu),通常指的是「代碼重構(gòu)」,起源于 Smalltalk 圈子。

        在日常工作中,我們把重構(gòu)既作為名詞又作為動詞來使用,作為名詞時,它的意思是:

        對軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,降低其修改成本。

        所以我們會說,「這里需要做一個重構(gòu)」,「這個重構(gòu)有點問題」等。

        而在其它時候,我們也會說:「我們來重構(gòu)一下這段代碼吧」,「我正在重構(gòu)一個遺留系統(tǒng)」,這時就是把重構(gòu)當做動詞在用,它的意思是:

        使用一系列重構(gòu)手法,在不改變軟件可觀察行為的前提下,調(diào)整其結(jié)構(gòu)。

        重構(gòu)本質(zhì)上是一種代碼整理技術(shù),這項技術(shù)使得代碼整理的效率更高,風險更小。

        如何做

        接下來從幾個方面來說說如何做重構(gòu):

        • 什么時候開始

        • 什么時候停止

        • 前提條件

        • 重構(gòu)的過程

        什么時候開始

        重構(gòu)不應(yīng)該是一個單獨的環(huán)節(jié),應(yīng)該融入到開發(fā)軟件編寫代碼的過程中,就像使用版本控制系統(tǒng)提交代碼一樣,是一個必須做的動作。

        你不會跟項目經(jīng)理說,我需要申請一段時間來提交代碼,所以也不用說服項目經(jīng)理給你時間重構(gòu)。

        你可以在開發(fā)新功能,修復(fù) Bug 的過程中就把重構(gòu)做了,除了你的程序員同伴,沒有人知道你做了什么。

        而他們會認為你做了一件了不起的事情,因為你讓代碼結(jié)構(gòu)更清晰了,以后添加新特性就會更容易,而 Bug 也無處藏身。

        如果你采用 TDD 的方式(測試驅(qū)動開發(fā)),那重構(gòu)已完全融入到了開發(fā)過程中。如果沒有采用 TDD,通常有四個時機可以考慮要不要重構(gòu):

        事不過三

        如果有段代碼讓你修改起來很不舒服,前兩次還可以忍耐,第三次就無需再忍了,果斷操起 IDE 重構(gòu)之。因為出現(xiàn)了三次修改,說明有很大概率以后還會修改,這是一筆劃算的投資。

        添加新功能

        有時候我們發(fā)現(xiàn)要添加一個新功能很難,我們可以對代碼做一些重構(gòu),讓添加新功能變得容易。

        修復(fù)缺陷

        在修 Bug 時,我們大部分的時間會花在定位 Bug 上,為什么這么難以找到呢?多半是因為代碼結(jié)構(gòu)不清晰,如果代碼在同一抽象層次上,每個方法都在 10 行以內(nèi),每個方法名和變量名都能清晰地表達意圖,Bug 就再無藏身之處。

        所以,通過重構(gòu)代碼,可以讓 Bug 自動浮現(xiàn)出來。

        代碼評審

        Code Review 已是一個廣泛采用的實踐,在 Code Review 時,其他程序員會提出代碼修改的意見,記錄下來,等 Code Review 結(jié)束之后就可以開始重構(gòu)了。

        什么時候停止

        重構(gòu)到什么時候,我們就認為可以停止了呢?

        有兩個標準可以參考,一個是「簡單設(shè)計」的四條原則:

        • 通過所有測試

        • 沒有重復(fù)

        • 表達意圖

        • 最少化程序元素(類,接口,變量,方法等)

        另一個是滿足《Clean Code》(整潔代碼)的要求。

        前提條件

        現(xiàn)代 IDE,尤其是 JetBrains 公司的一系列產(chǎn)品,支持常用的重構(gòu)手法,極大地降低了重構(gòu)的風險。但為了保證不改變軟件的可觀察行為,還是需要完善的測試。

        我也做過一些沒有測試代碼保護的重構(gòu),通常會加一個端到端測試以保證不破壞最重要的功能。實在很難編寫測試代碼,至少也要手工測試來保證重構(gòu)真的沒有改變軟件行為。

        另一個重要前提是,使用版本控制系統(tǒng),比如 Git。因為我們的重構(gòu)并不一定總是令人滿意,也有可能出現(xiàn)錯誤,導(dǎo)致軟件變得不可用,所以最好是小步提交,以保證可以隨時放棄變更,回到上一次滿意的狀態(tài)。

        重構(gòu)的過程

        重構(gòu)的基本步驟是:

        • 測試保護

        • 識別味道

        • 采用手法

        • 運行測試

        • 提交代碼

        測試保護

        如果沒有測試代碼,就要先添加測試代碼。如果有測試代碼,先運行一下,保證在開始重構(gòu)之前,測試是運行通過的。還要認真審查一下測試代碼,看是否有遺漏一些場景,有遺漏的話要補充遺漏的測試場景。

        識別味道

        怎么知道哪些代碼需要重構(gòu)呢?首先,代碼是可以工作的,我們并不能說它有問題,但它又不像我們期望的那樣好。受 Kent Beck 剛出生的女兒的使用的尿布的啟發(fā),Martin Fowler 和 Kent Beck 決定用「味道」這個詞來表示需要重構(gòu)的代碼。

        他們在《重構(gòu)》一書中列舉了 22 中常見的味道,如果你看《Clean Code》的話,會發(fā)現(xiàn)還有更多。不過,他們并沒有給出一個具體的標準,而是需要我們的直覺來判斷。

        比如多大的類算「過大的類」?多少行代碼算「過長的方法」?這些需要自行判斷,而直覺的形成有兩種方法,一是隨著編碼經(jīng)驗的增多自然形成,另一種更快的方式是大量閱讀優(yōu)秀的開源代碼,提高自己的代碼審美。

        《重構(gòu)》一書中的味道可以分為五類:

        • 膨脹劑

        • OO 使用不合理

        • 難以修改

        • 可有可無

        • 耦合

        書中都有詳細的解釋,這里不再贅述。

        發(fā)散式變化和散彈式修改是比較容易混淆的兩個味道。前者指一個類的職責過多,有很多因素會引起它的變化,具體的表現(xiàn)就是,不同的需求都會修改同一個文件,導(dǎo)致經(jīng)常沖突,不能順利地并行開發(fā)。

        后者指的是改一個需求要修改很多個文件,說明沒有把強內(nèi)聚的代碼歸攏到一起。

        大部分的注釋都是沒有必要的,注釋應(yīng)該描述「做了什么」和「為什么做」而不是「怎么做」,方法體內(nèi)的注釋基本都可以通過抽取方法并指定一個有意義的名字來解決。

        很多為了應(yīng)對未來需求變化而寫的代碼基本永遠不會被執(zhí)行。

        你可能發(fā)現(xiàn)了,有些味道是比較容易識別的,比如重復(fù)代碼,注釋等。

        而有些就比較高級,比如特性依戀,中間人等,要識別高級味道,需要理解面向?qū)ο蟮奶匦院驮O(shè)計原則。

        采用手法

        識別到味道之后,就要知道有什么對應(yīng)的手法可以消除這個味道,執(zhí)行完這個手法之后代碼會變成什么樣子。

        在《重構(gòu)》一書中,列舉了 66 個常用手法,可以分為六大類:

        • 重組函數(shù)

        • 搬移特性

        • 組織數(shù)據(jù)

        • 簡化條件

        • 簡化調(diào)用

        • 處理概括

        這些手法在書中都有詳細的講解,我就不在這里重復(fù)了。只整理出來,給大家一個宏觀的印象:

        運行測試

        在采用了手法修改代碼之后,就要執(zhí)行測試以確保真的沒有改變軟件的行為??赡苡袝r會發(fā)現(xiàn),做了重構(gòu)之后測試會失敗,但實現(xiàn)并沒有問題,我們需要修改測試代碼讓它成功。

        這就說明測試寫的不合理,給重構(gòu)帶來了負擔,所以我們測試的粒度要把握好,太細的粒度就會增加維護成本。

        比如,有些人會給每個私有方法都寫單元測試,那有可能采用「內(nèi)聯(lián)函數(shù)」這個手法之后這個方法就不存在了,就需要修改測試。

        這里說起來話就長了,以后再寫一篇如何寫有效的測試的文章吧。重點是重構(gòu)之后,一定要執(zhí)行測試,不管是手工測試或自動化測試。

        提交代碼

        最后,如果你采用了一個比較復(fù)雜的手法,或者即將采用一個復(fù)雜的手法,最好先提交一下代碼,以保證出現(xiàn)意外后能快速回滾,避免浪費時間。

        重構(gòu)要采取「小步快跑」的原則,盡量采用安全的手法,讓測試一直處于通過的狀態(tài)。從低級的壞味道開始,消除低級味道之后,高級味道才會浮現(xiàn)出來。

        進階

        重構(gòu)與設(shè)計的關(guān)系

        在沒有重構(gòu)這個技術(shù)之前,廣泛采用的是 Big Front Design,在開始編碼之前要進行非常詳細的設(shè)計,考慮應(yīng)對未來出現(xiàn)的各種變化。

        而有了重構(gòu)技術(shù)之后,前期設(shè)計的壓力就小了,畢竟可以隨時通過重構(gòu)來改善設(shè)計,應(yīng)對變化。

        所以你大可不必一上來就應(yīng)用《設(shè)計模式》把代碼搞復(fù)雜,先用簡單的實現(xiàn)滿足當前需求即可。等變化真正來臨時,再通過重構(gòu)技術(shù)調(diào)整設(shè)計,模式給我們提供了一個方向,但并不是最終目標。

        還記得簡單設(shè)計的四條原則嗎?通過測試,沒有重復(fù),表達意圖,最少元素。除了這四條原則,還有 SOLID,DRY,KISS 等設(shè)計原則。只要最終的代碼符合好的原則,干凈整潔沒有壞味道,管它符不符合某個模式呢?!

        大型遺留系統(tǒng)的重構(gòu)

        對于代碼上百萬,千萬行的遺留系統(tǒng),怎么重構(gòu)呢?滿地都是壞味道,一點點去重構(gòu),什么時候是個頭?

        這時,選擇哪些代碼來重構(gòu)就非常重要,影響到投資回報。如果對代碼進行分類,將會得出幾種類型:

        • 不會被執(zhí)行的爛代碼

        • 運行穩(wěn)定,基本不會改動的爛代碼

        • 經(jīng)常發(fā)現(xiàn) Bug 的爛代碼

        • 經(jīng)常需要變更的爛代碼

        不會被執(zhí)行的代碼,直接刪除就好了。運行穩(wěn)定的又不需要改動的,動它反而可能引入風險,當然,在時間充裕的情況下,還是可以重構(gòu)的。

        真正有價值,值得重構(gòu)的,投入產(chǎn)出比最高的,是經(jīng)常出問題和經(jīng)常會有需求變更的爛代碼。優(yōu)化了這部分代碼,可以減少 Bug 和進行需求變更的時間。

        總結(jié)

        好了。關(guān)于重構(gòu)我想分享的就是這些,我們來回顧一下:

        為什么要重構(gòu)?

        為了讓軟件始終可以維護,保證開發(fā)效率。

        什么是重構(gòu)?

        一種以可控的方式整理代碼的技術(shù),在不改變軟件可觀察行為的前提下改善其內(nèi)部結(jié)構(gòu)。

        什么時候開始?

        事不過三,添加功能,修復(fù) Bug,代碼評審時。

        什么時候停止?

        重構(gòu)到符合簡單設(shè)計四條原則的 Clean Code。

        前提條件

        測試保護,版本控制。

        重構(gòu)的過程

        運行測試,識別味道(常見的 22 種),采用手法(66 個),運行測試,提交代碼。

        重構(gòu)與設(shè)計的關(guān)系

        有了重構(gòu)技術(shù),我們不用在前期做非常詳細的設(shè)計,做適當?shù)脑O(shè)計,然后通過重構(gòu)讓設(shè)計浮現(xiàn)出來。不用在乎軟件是否符合模式,只要符合原則即可。

        大型遺留系統(tǒng)的重構(gòu)

        在經(jīng)常需要修改的爛代碼上做重構(gòu)才有最大收益。

        作者:Seaborn Lee

        https://gitbook.cn/books/591837cbc9b8f67d6a6a94df/index.html


        覺得本文對你有幫助?請分享給更多人

        關(guān)注「全棧開發(fā)者社區(qū)」加星標,提升全棧技能


        本公眾號會不定期給大家發(fā)福利,包括送書、學習資源等,敬請期待吧!

        如果感覺推送內(nèi)容不錯,不妨右下角點個在看轉(zhuǎn)發(fā)朋友圈或收藏,感謝支持。


        好文章,留言、點贊、在看和分享一條龍吧??

        瀏覽 32
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大香蕉伊人6 | www.jiba | 亚洲日韩在线视频 | 国产精品一卡二卡 | 丰满人妻一区二区 | 女星裸体看个够无遮挡 | 免费无码毛片 | 岳啊轻点我高潮了 | 三级色网 | 免费 无码 国产在线53 |