重構之十六字心法

文/ThoughtWorks 王健

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

重構的威力

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

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

需求在變,語言在變,框架在變,工具在變,框架在變,架構在變,趨勢在變,甚至連組織結構都在不斷的變化。

隨著變化的不斷產生,軟件變得越來越復雜。就像《架構腐化之謎》中提到的一樣,我們的軟件也會像一個生命體,經歷從新生到衰老腐化的過程。而重構就像是一次手術,通過優化內部結構,減慢腐化衰老,讓軟件“青春永駐”,可見重構的威力。

重構教會了我如何通過高效安全地改善內部設計以使之適應外部的不確定性和頻繁變化。

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

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

合格的重構

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

對于什么是重構,《重構》書中已經有明確的定義,分名詞和動詞兩種形式。

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

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

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

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

可以反思一下,我們平時自認為的那些重構,是否都符合了以上的這些要求?

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

在我的眼里,這些都不是合格的重構,甚至都不能稱之為重構,好的重構應該像一邊開車一邊換輪胎一樣,保證系統隨時可工作的前提下,還可以對其結構做出安全高效的調整。

可見重構并不簡單,那要怎樣才能達到上述的那些要求呢?

重構的心法

在過去的幾年,我一直在學習和思考重構的各種手法。從剛開始的亂改一氣,到學習基于IDE和插件的各種快捷鍵流的重構手法,以及研究如何通過組合各種基礎重構手法形成“連招”,從而快速實現更復雜的重構過程。

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

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

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

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

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

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

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

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

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

總結

重構可以使軟件更容易地被修改和被理解。通過不斷地改進軟件設計以達到簡單設計的目標,減少由于設計與業務的不匹配帶來的架構與設計腐化。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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