
文章首發(fā)于公眾號「架構(gòu)師指南」及個人博客 shuyi.tech,歡迎關(guān)注訪問。
對于剛?cè)腴T的編程者來說,《重構(gòu)》是一本不錯的讀物。它能給你帶來一些重構(gòu)思想上的改變,告訴你為什么要重構(gòu),應(yīng)該怎么做重構(gòu)。本文基于《重構(gòu)》一書,整理重構(gòu)所需的「思想」與「技巧」上的準(zhǔn)備。
思想篇指的是對于重構(gòu)的認(rèn)識,理解這些思想能夠讓你更好地做好重構(gòu)。而技巧篇指的是具體重構(gòu)時的一些技巧,能夠讓你知道怎么寫出更好的代碼。
思想篇
重構(gòu)之前先建立測試用例
重構(gòu)的第一步,是為即將修改的代碼建立一組可靠的測試用例。預(yù)見建立好的測試用例,是你的安全繩,它能告訴你工作是否完成了,是否存在可能的缺陷。
重構(gòu)的價值
重構(gòu)可以改進(jìn)軟件的設(shè)計。就像在不斷整理代碼一樣,經(jīng)常性的重構(gòu)可以幫助代碼維持自己該有的形態(tài)。重構(gòu)使得軟件更容易理解,不要讓幾個月之后其他人(甚至自己)也讀不懂你的代碼,清晰易懂的代碼能讓你更快理解代碼的意圖。重構(gòu)能幫助找到bug,因為重構(gòu)是小步快跑的,每一步都有一個獵手(測試用例)幫你抓到獵物(bug)。
好的重構(gòu),最終能幫你提高編程速度,提高編程帶來的愉悅感。
什么時候重構(gòu)?
什么時候重構(gòu)?第一次只管去做,第二次會反感,第三次應(yīng)該重構(gòu)。事不過三,三則重構(gòu)。
專門撥出時間重構(gòu)是不可能的,我們需要在日常工作中不斷地重構(gòu)。但是還沒開始有重復(fù)的功能,就想著重構(gòu),那太可笑了。但是重復(fù)的代碼或者代碼有問題,超過三次之后還不動手,那么就有點(diǎn)偷懶了。
什么時候不重構(gòu)?
當(dāng)現(xiàn)有代碼根本不能正常運(yùn)作的時候,你應(yīng)該重寫,而不是重構(gòu)。
重構(gòu)應(yīng)該是一個習(xí)慣
重構(gòu)應(yīng)該是一種工作習(xí)慣,在日常工作中一點(diǎn)點(diǎn)重構(gòu),而不是妄想有專門的時間重構(gòu)。我們曾經(jīng)進(jìn)行的一些大型重構(gòu),需要數(shù)月甚至數(shù)年的時間。如果需要給一個運(yùn)行中的系統(tǒng)添加功能,你不可能讓系統(tǒng)停止2個月去重構(gòu)。你只能一點(diǎn)點(diǎn)地做你的工作,今天一點(diǎn)點(diǎn),明天一點(diǎn)點(diǎn)。
如何測試?
我們的時間總是有限的,測試你最擔(dān)心出錯的部分,這樣你能得到最大的收益。測試的時候,尋找邊界條件,集中火力測試那里!
什么時候取消重構(gòu)?
如果你感覺到重構(gòu)失控了,那么最好的辦法是取消重構(gòu),回到你的安全區(qū)去。等你重新能掌控的時候,再來做重構(gòu)。
重構(gòu)的團(tuán)隊意識
進(jìn)行大規(guī)模重構(gòu)時,有必要為整個開發(fā)團(tuán)隊建立共識。整個團(tuán)隊都必須意識到:有一個大型重構(gòu)正在進(jìn)行,每個人都應(yīng)該相應(yīng)地安排自己的行動。
設(shè)計模式幫助你重構(gòu)
學(xué)習(xí)設(shè)計模式可以很好地幫助你重構(gòu),它能在適當(dāng)?shù)膱龊蠋椭愠休d復(fù)雜的業(yè)務(wù)。但你不應(yīng)該簡單地了解,而是要多對比各個設(shè)計模式之間的區(qū)別,它們解決了什么問題,適用于什么場合。
技巧篇
不要出現(xiàn)重復(fù)代碼
當(dāng)出現(xiàn)重復(fù)代碼時,你應(yīng)該提取出公共方法。我想這個說得已經(jīng)足夠清楚了,當(dāng)出現(xiàn)重復(fù)代碼的時候就需要想想:我是否需要抽離出重復(fù)的代碼?
不要出現(xiàn)過長、過短的函數(shù)
當(dāng)函數(shù)過長,你應(yīng)該根據(jù)業(yè)務(wù)邏輯提煉出多個函數(shù)。那一個函數(shù)多少行算是長呢?按我個人理解,一個函數(shù)在 20-50 行是比較合適的。但這也只是一個經(jīng)驗值,最根本的判斷標(biāo)準(zhǔn)是:別人閱讀你的代碼的時候,是否能很清晰、很方便地讀懂。 如果你寫得很長,但是別人讀得時候很舒服,那么也可以。
要注意函數(shù)過短也會帶來閱讀的困難,他會讓你多次跳轉(zhuǎn),打斷你的閱讀思路。所以如果一個函數(shù)內(nèi)容過短,你需要考慮是否去掉這個函數(shù)。簡單地說,你還是應(yīng)該根據(jù)業(yè)務(wù)邏輯結(jié)構(gòu)化,將每塊業(yè)務(wù)邏輯放到合適的函數(shù)中。
不要出現(xiàn)過大的類
當(dāng)類過大,你應(yīng)該考慮是否能拆分出多個類。或者你應(yīng)該考慮,你的類抽象體系是否出現(xiàn)了問題。一個過大的類與過長的函數(shù)一樣,會讓人感覺到壓抑、難于讀懂。
不要讓參數(shù)過長
當(dāng)參數(shù)列過長,你應(yīng)該使用對象參數(shù)。
提煉發(fā)散式變化
因為一個變化,而需要修改多個地方,這說明出現(xiàn)了發(fā)散式變化,你需要考慮將變動的代碼合并在一起。
提煉對象
總是綁在一起出現(xiàn)的數(shù)據(jù),需要把他們提煉到一個獨(dú)立對象中。
引入解釋性變量
不要讓你的變量或表達(dá)式?jīng)]有語義,必要時引入解釋性變量。很多人會習(xí)慣性地用 flag 去承載一個表達(dá)式的值,但這并不是一個好的習(xí)慣。變量命名還是應(yīng)該更加語義化,這樣我們能更加清晰地明白這個變量的作用。
搬移函數(shù)
一個函數(shù)被另一個類調(diào)用得很頻繁,那你可能得考慮把這個函數(shù)搬移到另一個類中。
搬移字段
一個字段被另一個類用得很頻繁,或許你改考慮把這個字段搬移到另一個類中。
提煉類、簡化類
某個類做了應(yīng)該由兩個類做的事情,此時應(yīng)該提煉出一個新類,然后用組合關(guān)系組合起來。這其實與 SOLID 原則想契合,一個類應(yīng)該是單一職責(zé)的,如果某個類做了兩個類的事情,那說明其承擔(dān)的職責(zé)就復(fù)雜了,因此需要抽離出一個新類出來。
而如果一個類并沒有太多內(nèi)容,這時候就應(yīng)該考慮是否去掉這個類,優(yōu)化整個類結(jié)構(gòu)。
參考資料
- 除舊迎新,試試《系統(tǒng)重構(gòu)與遷移指南》 - 知乎
- 五個簡單的原則,帶你寫出整潔代碼 - 知乎
- GitHub - phodal/migration: 《系統(tǒng)重構(gòu)與遷移指南》手把手教你分析、評估現(xiàn)有系統(tǒng)、制定重構(gòu)策略、探索可行重構(gòu)方案、搭建測試防護(hù)網(wǎng)、進(jìn)行系統(tǒng)架構(gòu)重構(gòu)、服務(wù)架構(gòu)重構(gòu)、模塊重構(gòu)、代碼重構(gòu)、數(shù)據(jù)庫重構(gòu)、重構(gòu)后的架構(gòu)守護(hù)
- 技術(shù)債治理的四條原則 - ThoughtWorks 洞見
- 31 天重構(gòu)指南 - InfoQ
- VIP?。。。efactoring Day 1 : Encapsulate Collection · Los Techies
- 不要讓 “Clean Code” 更難維護(hù),請使用 “Rule of Three”-InfoQ