重構(gòu)之十六字心法

文/ThoughtWorks 王健

這篇文章是我寫過的所有文章里最難產(chǎn)的一篇,前前后后斟酌醞釀了好幾個月。因為重構(gòu)對于我來講真的太重要也太深刻了,包含的內(nèi)容和想說的也太多了。如果說這幾年自己覺得在哪些方面的收獲最大的話,非重構(gòu)莫屬了。

重構(gòu)的威力

軟件開發(fā)的難點在于不確定性,前幾天邱大師剛寫了一篇《軟件開發(fā)為什么很難》就提到

軟件的復(fù)雜性來自于大量的不確定性,而這個不確定事實上是無法避免的。

需求在變,語言在變,框架在變,工具在變,框架在變,架構(gòu)在變,趨勢在變,甚至連組織結(jié)構(gòu)都在不斷的變化。

隨著變化的不斷產(chǎn)生,軟件變得越來越復(fù)雜。就像《架構(gòu)腐化之謎》中提到的一樣,我們的軟件也會像一個生命體,經(jīng)歷從新生到衰老腐化的過程。而重構(gòu)就像是一次手術(shù),通過優(yōu)化內(nèi)部結(jié)構(gòu),減慢腐化衰老,讓軟件“青春永駐”,可見重構(gòu)的威力。

重構(gòu)教會了我如何通過高效安全地改善內(nèi)部設(shè)計以使之適應(yīng)外部的不確定性和頻繁變化。

重構(gòu)威力無邊,就像是武俠小說中的一件插在石頭上的上古神器,但同樣也不是一般人可以輕松駕馭的。如果運用不當(dāng),造成的損害也會同樣巨大。

如何將重構(gòu)這件神器運用自如,發(fā)揮其最大的威力,也是我一直在探尋的,即重構(gòu)的手法和心法。

合格的重構(gòu)

在談手法和心法之前,可能很多人會有疑惑,覺得重構(gòu)并不像你說的那么難啊,我們每天都在做,就是改改代碼改改設(shè)計,哪有你說的那么邪乎。那我就先來講講我認(rèn)為怎么樣才算是一次合格的重構(gòu)。

對于什么是重構(gòu),《重構(gòu)》書中已經(jīng)有明確的定義,分名詞和動詞兩種形式。

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

就像“看板”不是“我們看到的那個白板”一樣,“重構(gòu)”也不是“重新修改代碼”那么簡單。

我就看到過太多打著重構(gòu)的幌子,把系統(tǒng)改的面目全非,最后出了問題直接甩鍋到重構(gòu)身上的場景了。那怎樣才算是一次合格的重構(gòu)呢?我覺得至少需要做到以下幾點:

  • 消除味道:一個重構(gòu)應(yīng)該是從識別一個壞味道(Bad Smell)開始,以消除一個壞味道結(jié)束,任何不以消除壞味道為目標(biāo)的重構(gòu)都是耍流氓。
  • 始終工作:即重構(gòu)定義中的“在不改變軟件可觀察行為的前提下”,說白了就是重構(gòu)過程不能破壞甚至改變軟件外在功能。
  • 持續(xù)集成:不需要為重構(gòu)單建分支,重構(gòu)過程可以做到Feature開發(fā)在同一分支上持續(xù)集成持續(xù)交付。
  • 隨時中止:例如一個方法重命名,需要修改100個調(diào)用點,當(dāng)改到50個的時候有個緊急的Feature,我可以隨時暫停重構(gòu),立即切換到Feature開發(fā)上,且不需要回滾已做的重構(gòu)。
  • 斷點續(xù)傳:還是上邊的例子,假如我已經(jīng)完成了緊急Feature的開發(fā),可以隨時繼續(xù)之前的重構(gòu),完成剩下50個調(diào)用點的重命名。
  • 過程可逆:對于重構(gòu),經(jīng)常有人會問:你怎么保證重構(gòu)就會更好而不是更壞呢?重構(gòu)的偉大就在于他跳出了對錯之爭,將關(guān)注點放到如何快速平滑安全的變化上,當(dāng)然也包括反向重構(gòu)。所以我的回答是:無法保證,但是我可以一分鐘就重構(gòu)回來。如果仔細(xì)看,《重構(gòu)》書里的所有重構(gòu)手法都是雙向的,比如“Extract Method”和“Inline Method”。

可以反思一下,我們平時自認(rèn)為的那些重構(gòu),是否都符合了以上的這些要求?

  • 多少次我們打著重構(gòu)的旗號,七零八碎,無法復(fù)原。
  • 多少次我們打著重構(gòu)的旗號,分支開發(fā),集成困難。
  • 多少次我們打著重構(gòu)的旗號,半途而廢,迷途難返
  • 多少次我們打著重構(gòu)的旗號,孤注一擲,進(jìn)退兩難。

在我的眼里,這些都不是合格的重構(gòu),甚至都不能稱之為重構(gòu),好的重構(gòu)應(yīng)該像一邊開車一邊換輪胎一樣,保證系統(tǒng)隨時可工作的前提下,還可以對其結(jié)構(gòu)做出安全高效的調(diào)整。

可見重構(gòu)并不簡單,那要怎樣才能達(dá)到上述的那些要求呢?

重構(gòu)的心法

在過去的幾年,我一直在學(xué)習(xí)和思考重構(gòu)的各種手法。從剛開始的亂改一氣,到學(xué)習(xí)基于IDE和插件的各種快捷鍵流的重構(gòu)手法,以及研究如何通過組合各種基礎(chǔ)重構(gòu)手法形成“連招”,從而快速實現(xiàn)更復(fù)雜的重構(gòu)過程。

隨著對于基于IDE的快捷鍵重構(gòu)手法越來越嫻熟,在IDE和插件的幫助下,我的重構(gòu)手法越來越華麗而迅捷,在沾沾自喜的同時心里也慢慢萌生了一些質(zhì)疑:難道這就是重構(gòu)么?如果沒有IDE沒有了插件,我還會做重構(gòu)么?如何用編輯器(Vim,Emacs)做重構(gòu)?重構(gòu)只是代碼級別的么?數(shù)據(jù)庫如何重構(gòu)呢?系統(tǒng)架構(gòu)如何重構(gòu)呢?工具框架如何重構(gòu)呢?微服務(wù)架構(gòu)下的服務(wù)重構(gòu)呢?公司組織重構(gòu)呢?

這種感覺就像是武俠小說中的某個柔弱書生,無意中掉到了一個懸崖下,找到了一本武林秘籍,照著上邊的招式練了練就自以為已絕學(xué)在身,結(jié)果出去雖然能招架一時,但禁不住更大的挑戰(zhàn)。被打的體無完膚后,重新掏出那本秘籍,收起浮躁,懷著誠敬之心努力去參悟那些招式背后更深的哲理,也就是所謂的心法。此時對于我來說,而那本武林秘籍就叫做《重構(gòu)》。

在帶著這些疑問重讀《重構(gòu)》的過程中,我欣喜地發(fā)現(xiàn)書中那些細(xì)致入微但看似笨拙拖沓的重構(gòu)手法(例如Rename,使用現(xiàn)代IDE一個快捷鍵就可以搞定,但是老馬用了很多步驟才完成),其實都蘊含著重構(gòu)最重要最基本的原則和思路,只要按著這些原則去做,無論什么層次的重構(gòu):代碼重構(gòu)、架構(gòu)重構(gòu)、服務(wù)重構(gòu)甚至是組織重構(gòu),都可以做到上面提到的一個合格重構(gòu)的基本要求,即平滑安全可??衫m(xù)。

把其中的原則思路抽取出十六個字,即所謂的:重構(gòu)十六字心法。

解釋起來也很簡單,往往我們做”重構(gòu)“的時候就是在舊的結(jié)構(gòu)(這里的結(jié)構(gòu)可以是一個方法、一個對象、一個服務(wù)、一個數(shù)據(jù)庫、一個服務(wù)甚至是一個組織結(jié)構(gòu))上直接修改,導(dǎo)致系統(tǒng)長時間處于一個中間不可用狀態(tài),這個狀態(tài)持續(xù)的時間越長,”重構(gòu)“失敗的可能性和負(fù)面影響就會越大。

而《重構(gòu)》告訴我們,做內(nèi)部結(jié)構(gòu)調(diào)整時,先不要直接修改舊的結(jié)構(gòu),保持舊的結(jié)構(gòu)不變,先按照新的設(shè)計思路創(chuàng)建一個新的結(jié)構(gòu),因為這個過程中對于舊的內(nèi)部結(jié)構(gòu)沒有任何影響,所以是安全的,可持續(xù)集成的。當(dāng)新的結(jié)構(gòu)構(gòu)件完成時,我們再把對于舊結(jié)構(gòu)的依賴一個個的切換到新的結(jié)構(gòu)上,即所謂的”一步切換“。最后當(dāng)確認(rèn)所有對于舊的結(jié)構(gòu)都切換到新的結(jié)構(gòu)上,而且沒有問題后,再將已經(jīng)沒有任何引用的舊結(jié)構(gòu)刪除掉,完成整個重構(gòu)過程。

這里的“一步切換”并不是說整個重構(gòu)的切換過程必須是一步完成的,例如前面重命名的例子,100個調(diào)用點的切換可能是分多次完成的,在這個例子里一步切換指的是每一個調(diào)用點的切換過程。這個切換過程是最容易暴露出問題的,所以越簡單越快速越好,一旦出現(xiàn)了問題,就快速的切換回舊的結(jié)構(gòu)后再慢慢排查問題,從而實時保證系統(tǒng)的可用性。

大道至簡,一旦領(lǐng)悟并掌握了這個心法,就發(fā)現(xiàn)自己一下從之前狹義的代碼重構(gòu)中跳脫出來,任何廣義上的重構(gòu)都立刻變得有章可循。

在架構(gòu)重構(gòu)中常用的抽象分支(BranchByAbstraction),以及在微服務(wù)架構(gòu)下服務(wù)重構(gòu)常用到的絞殺者模式,其實都是這種原則的一種體現(xiàn)。

總結(jié)

重構(gòu)可以使軟件更容易地被修改和被理解。通過不斷地改進(jìn)軟件設(shè)計以達(dá)到簡單設(shè)計的目標(biāo),減少由于設(shè)計與業(yè)務(wù)的不匹配帶來的架構(gòu)與設(shè)計腐化。

掌握了重構(gòu)的手法和心法,會讓重構(gòu)變得更加簡單安全高效可控,從而真正的發(fā)揮出其巨大的威力,讓我們的軟件永葆青春。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇文章是我寫過的所有文章里最難產(chǎn)的一篇,前前后后斟酌醞釀了好幾個月。因為重構(gòu)對于我來講真的太重要也太深刻了,包含...
    王健_TW閱讀 4,857評論 7 39
  • 重構(gòu)的威力 軟件的復(fù)雜性來自于大量的不確定性,而這個不確定事實上是無法避免的。 需求在變,語言在變,軟件的復(fù)雜性來...
    蠟筆曉曉新閱讀 606評論 0 0
  • 重構(gòu)的定義: 所謂重構(gòu)是這樣一個過程:在不改變代碼外在行為的前提下,對代碼做出修改,以改進(jìn)程序的內(nèi)部結(jié)構(gòu)。 重構(gòu)是...
    天色將變閱讀 1,821評論 0 1
  • 為什么要重構(gòu) 你可能正在面對一個遺留系統(tǒng),增加一個需求要改動好幾個文件,定位 Bug 經(jīng)常要花掉一整天時間,修復(fù)一...
    李浪溪_WaterLee閱讀 5,779評論 7 30
  • 引言 Martin Fowler的《重構(gòu):改善既有代碼的設(shè)計》一書從問世至今已有十幾年時間了,按照計算機(jī)領(lǐng)域日新月...
    MagicBowen閱讀 5,333評論 3 10

友情鏈接更多精彩內(nèi)容