我們一直在尋找各種方法來(lái)清理代碼、降低復(fù)雜性和改善功能。而重構(gòu)為我們指明了前進(jìn)的方向。
1、什么是重構(gòu)?
Martin Fowler曾出版了兩本有關(guān)重構(gòu)的書籍,他認(rèn)為:
重構(gòu)指的是,在不改變代碼的外部行為,只改善其內(nèi)部結(jié)構(gòu)的方式下,修改軟件系統(tǒng)的過(guò)程。重構(gòu)是一種有條理的清理代碼的方式,可以最大程度地減少引入bug的機(jī)會(huì)。本質(zhì)上,重構(gòu)意味著在代碼編寫完成后,改進(jìn)代碼的設(shè)計(jì)。
2、重構(gòu)有什么好處?
重構(gòu)源代碼有數(shù)不清的好處。首先,重構(gòu)可以將混亂、不正確和/或重復(fù)的代碼轉(zhuǎn)換成整潔的代碼。它可以解決多位開(kāi)發(fā)人員協(xié)同工作時(shí)可能引發(fā)的代碼標(biāo)準(zhǔn)化問(wèn)題。重構(gòu)可以提高可讀性,改善源代碼的可維護(hù)性以及整體結(jié)構(gòu)和功能。重構(gòu)可以使代碼更易于擴(kuò)展和添加新功能。刪除不必要的代碼(比如重復(fù)代碼)可以減少代碼所使用的內(nèi)存,并加快執(zhí)行速度。
例如,在2014年,Kickstarter的工程師面臨著一個(gè)巨大的挑戰(zhàn):由于用戶數(shù)量呈指迅速增長(zhǎng),導(dǎo)致查詢性能下降。為此,他們將MySQL查詢重構(gòu)為Redis,減少了100毫秒的加載時(shí)間,從而減少了加載時(shí)間的差異并提高了網(wǎng)站的整體速度。
3、技術(shù)負(fù)債與重構(gòu)
簡(jiǎn)而言之,重構(gòu)是消除或減少技術(shù)負(fù)債的一種方式。
重構(gòu)對(duì)于長(zhǎng)期維持的代碼質(zhì)量、安全性和性能至關(guān)重要。如果沒(méi)有定期的重構(gòu),開(kāi)發(fā)人員就會(huì)承受巨大的技術(shù)負(fù)債。重構(gòu)代碼的機(jī)會(huì)越少,技術(shù)負(fù)債就會(huì)越多,開(kāi)發(fā)新功能也會(huì)變得越來(lái)越難。
4、重構(gòu)的指標(biāo)
我們可以通過(guò)各種指標(biāo),衡量重構(gòu)代碼的優(yōu)先級(jí)。在指標(biāo)的幫助下,我們可以有條不紊地計(jì)劃重構(gòu),每一次都專心完成最重要的任務(wù)。
此外,你需要通過(guò)指標(biāo)來(lái)衡量重構(gòu)的效果。我們不僅需要重構(gòu)低效的代碼,而且還可以通過(guò)修改低效代碼增加價(jià)值。為了獲得真正的價(jià)值,你需要進(jìn)行測(cè)試,包括單元測(cè)試和功能測(cè)試。除此之外,還有一些其他方面的指標(biāo),比如發(fā)現(xiàn)的bug數(shù)減少,以及降低循環(huán)復(fù)雜性(重構(gòu)的目標(biāo)是降低復(fù)雜性)。高度復(fù)雜的方法或功能(比如超過(guò)350行的方法或功能)就是良好的重構(gòu)對(duì)象。
此外,我們還需要考慮,如何將重構(gòu)融合到更廣泛的團(tuán)隊(duì)目標(biāo)或有關(guān)工作流和任務(wù)的里程碑中。
5、代碼重構(gòu)示例
代碼重構(gòu)的示例非常多,為了簡(jiǎn)潔起見(jiàn),我們介紹以下幾種:
紅色,綠色和重構(gòu)
重構(gòu)與單元測(cè)試息息相關(guān)。最常見(jiàn)的形式之一就是敏捷方法固有的測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(Test-Driven Development,即TDD)。你可以在編寫代碼之前先編寫測(cè)試。從本質(zhì)上來(lái)說(shuō),應(yīng)該由測(cè)試來(lái)驅(qū)動(dòng)程序,說(shuō)明代碼應(yīng)該執(zhí)行的操作。
紅色,綠色和重構(gòu)是測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的一個(gè)示例:
紅色:編寫沒(méi)有實(shí)現(xiàn)代碼的測(cè)試套件,必然會(huì)失敗。
綠色:編寫實(shí)現(xiàn)代碼,剛剛好可以通過(guò)測(cè)試套件。
重構(gòu):尋找優(yōu)化和改進(jìn)代碼的方法。
提取方法(又名提取函數(shù))
將代碼片段從現(xiàn)有方法移到新方法中,而新方法的名稱明確說(shuō)明了其功能。這種技術(shù)有助于降低復(fù)雜性并提高代碼的可讀性,另外《Java開(kāi)發(fā)手冊(cè)(嵩山版)》你需要好好看下。
提取變量
如果遇到難以理解的表達(dá)式,或者該表達(dá)式在整個(gè)代碼中重復(fù)了多次,則可以通過(guò)提取變量重構(gòu),將表達(dá)式或其中一部分放入一個(gè)復(fù)雜度較低且更易于理解的變量中。這樣可以減少?gòu)?fù)雜性和代碼重復(fù)。
按抽象建立分支
按抽象建立分支可以逐步對(duì)軟件系統(tǒng)進(jìn)行大規(guī)模地修改,而你則可以一邊修改代碼,一邊定期發(fā)布系統(tǒng)。這種方法可以降低在分支上重構(gòu)代碼的復(fù)雜性,避免在合并代碼時(shí)出現(xiàn)問(wèn)題。
方法組合
代碼過(guò)長(zhǎng)不便于理解,而且也不方便修改。方法組合指的是一系列的操作,將方法改成順序結(jié)構(gòu)并刪除重復(fù)的代碼。這些操作包括內(nèi)聯(lián)方法、內(nèi)聯(lián)模板、用查詢代替模板、拆分臨時(shí)變量以及刪除對(duì)參數(shù)的賦值等。
6、重構(gòu)代碼的工具
你需要專業(yè)的重構(gòu)工具嗎?Martin Fowler表示,自動(dòng)化的工具有幫助但不是必需的。另外,Java 工具系列教程全部整理好了,微信搜索互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)發(fā)送:2T,可以在線閱讀。
他指出:
“許多語(yǔ)言都有IDE,可以自動(dòng)執(zhí)行許多常見(jiàn)的重構(gòu)。這些是非常有價(jià)值的工具,可以幫助我更快地重構(gòu)代碼。但是,這些工具不是必不可少的,我經(jīng)常在沒(méi)有工具支持的情況下編寫程序,每次只邁出一小步,并通過(guò)頻繁的測(cè)試來(lái)發(fā)現(xiàn)錯(cuò)誤?!?/p>
許多開(kāi)發(fā)環(huán)境都可以自動(dòng)化重構(gòu),一些常見(jiàn)的重構(gòu)工具包括:
Visual studio intellicodeEclipse IDESpring Tool Suite 4RiderIntelliJ IDEASonarQube
7、重構(gòu)與工程經(jīng)理的難題
為了解決引發(fā)重構(gòu)需求的問(wèn)題,首先我們需要弄清楚公司的運(yùn)營(yíng)方式。另外《Java開(kāi)發(fā)手冊(cè)(嵩山版)》你需要好好看下。在著手重構(gòu)之前,請(qǐng)先回答下列幾個(gè)問(wèn)題:
哪些任務(wù)最優(yōu)先?開(kāi)發(fā)的速度如何?開(kāi)發(fā)人員是否感覺(jué)到了快速交付代碼的壓力?解決技術(shù)負(fù)債的流程都有哪些?實(shí)施了哪些類型的代碼審核?團(tuán)隊(duì)成員是否具備適當(dāng)?shù)闹貥?gòu)技能?公司的文檔標(biāo)準(zhǔn)是什么?
如果不解決引發(fā)重構(gòu)需求的根本問(wèn)題,那么問(wèn)題只會(huì)愈演愈糟。
8、高級(jí)管理層對(duì)重構(gòu)的支持
你們公司可能并沒(méi)有在基礎(chǔ)設(shè)施和維護(hù)上投入太多資金。
可能會(huì)有人說(shuō),應(yīng)該將花費(fèi)在重構(gòu)上的時(shí)間投入到新功能開(kāi)發(fā)上。
但是,我們?nèi)匀粦?yīng)該看一看重構(gòu)的好處,以及它們與工作流程、客戶、收入和業(yè)務(wù)增長(zhǎng)的關(guān)系。重構(gòu)得當(dāng)可以改善代碼,交付有效更新以及急需的功能,從而吸引新客戶和回頭客。即使在成功發(fā)布產(chǎn)品之后,軟件公司也可以通過(guò)這種方式保持競(jìng)爭(zhēng)力。
為了獲取高層管理的支持,還有一個(gè)更好的方法,即量化團(tuán)隊(duì)當(dāng)前花費(fèi)在修復(fù)原始代碼中的錯(cuò)誤或bug上的時(shí)間。具體一點(diǎn),比如每天一個(gè)小時(shí)?每天兩個(gè)小時(shí)?持續(xù)記錄一個(gè)星期,你就會(huì)驚訝地發(fā)現(xiàn)原來(lái)團(tuán)隊(duì)每年需要花費(fèi)數(shù)周或數(shù)月時(shí)間來(lái)修復(fù)遺留的代碼。
9、團(tuán)隊(duì)支持與重構(gòu):一個(gè)Sprint還是馬拉松?
很難在團(tuán)隊(duì)內(nèi)部開(kāi)展重構(gòu)工作?提及重構(gòu)就會(huì)哀聲載道?順利開(kāi)展重構(gòu)的最重要的標(biāo)志就是有計(jì)劃、有目標(biāo)以及有文檔記錄的行動(dòng)。Ron Jeffries(極限編程的三大創(chuàng)始人之一)將重構(gòu)比喻為清道:
“花些時(shí)間清出一條道來(lái),那么下一次我們就可以直奔我們要構(gòu)建的下一個(gè)功能,而無(wú)需繞過(guò)雜草和灌木叢?!?/p>
但是,他強(qiáng)調(diào)指出,糟糕的代碼需要花費(fèi)很長(zhǎng)的時(shí)間來(lái)清理,而且重構(gòu)應(yīng)該經(jīng)過(guò)深思熟慮:
“如果我們只改進(jìn)手頭的代碼,而忽略目前不涉及的代碼,那么以后必然會(huì)走回頭路?!?/p>
在同一個(gè)Sprint中,我們經(jīng)常發(fā)現(xiàn)后面的功能用到了我們之前清理過(guò)的代碼。我們就會(huì)立即享受重構(gòu)的好處。如果我們等積攢了一堆技術(shù)負(fù)債,再開(kāi)始重構(gòu),那么我們享受的好處會(huì)延遲,甚至可能會(huì)在一些沒(méi)大有用的地方浪費(fèi)精力。
產(chǎn)品工程師兼首席技術(shù)官Andreas Klinger是Fix-it Friday的粉絲,他表示:
“Fix-it Friday的規(guī)則很簡(jiǎn)單:除非當(dāng)前的項(xiàng)目十萬(wàn)火急,否則周五的工作就應(yīng)該是重構(gòu)。讓工程師選擇他們的工作。我們不應(yīng)該因?yàn)槲⒂^管理而抹殺這種樂(lè)趣。有些人會(huì)嘗試新的庫(kù)。有些人會(huì)修復(fù)積壓的bug。這兩種工作都很好。我們嘗試鼓勵(lì)大家平衡這些任務(wù)?!?/p>
無(wú)論采用哪種方法,你都需要慎重思考,詢問(wèn)團(tuán)隊(duì)哪些代碼最影響他們的效率。
修復(fù)哪些代碼對(duì)你的其他代碼產(chǎn)生的影響最大?解決哪些問(wèn)題得到的回報(bào)最多?你不太可能找到一整塊專門的時(shí)間來(lái)重構(gòu)代碼,重構(gòu)代碼必然會(huì)犧牲你花費(fèi)在其他項(xiàng)目上的時(shí)間,但請(qǐng)不要低估定期堅(jiān)持開(kāi)展小范圍的重構(gòu)帶來(lái)的影響。聚沙成塔,集腋成裘,最終你會(huì)獲得豐厚的回報(bào)。
10、文檔與重構(gòu)
標(biāo)準(zhǔn)化命名約定之類的文檔可以讓每個(gè)人都達(dá)成共識(shí)。Xerox的高級(jí)開(kāi)發(fā)人員的研究發(fā)現(xiàn),缺乏文檔是重構(gòu)最大的難題之一。
記錄重構(gòu)的工作內(nèi)容不僅可以記錄花費(fèi)的時(shí)間,而且還可以為將來(lái)的團(tuán)隊(duì)成員提供說(shuō)明。另外,關(guān)注公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師,在后臺(tái)回復(fù):2T,可以獲取我整理的 Java 系列面試題和答案,非常齊全。
最后,你還通過(guò)文檔記錄下自己的成功:重構(gòu)帶來(lái)的最大成功是什么?這些可以成為代碼審核的考慮因素嗎?