1. 程式人生 > >重構之十六字心法

重構之十六字心法

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

重構的威力

軟體開發的難點在於不確定性,同事邱俊濤剛寫了一篇《軟體開發為什麼很難》就提到

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

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

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

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

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

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

合格的重構

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

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

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

:使用一系列重構手法,在不改變軟體可觀察行為的前提下,調整其結構。

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

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

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

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

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

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

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

重構的心法

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

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

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

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

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

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

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

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

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

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

總結

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

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