(文末福利)如果程式碼莫名其妙跑起來了,就不要去動它了……嗎?

關注公眾號並回復「重構」

獲取《重構:改善既有程式碼

的設計》超詳細思維導圖

雖然程式碼還是可以跑,但是各種規則越來越複雜、核心繼承體系越來越凌亂、系統的維護工作越來越重……

1999 年,Martin Fowler 作為技術顧問造訪了一個專案,他建議專案經理好好整理這些亂糟糟的程式碼。然而,專案經理表示:算了吧

(文末福利)如果程式碼莫名其妙跑起來了,就不要去動它了……嗎?

六個月後,這個專案宣告失敗,因為程式碼太複雜難以除錯,效能也達不到要求。

這件事給 Martin 留下很深的印象,隨後,他寫下了

《重構:改善既有程式碼的設計》

(文末福利)如果程式碼莫名其妙跑起來了,就不要去動它了……嗎?

《重構》出版 22 年後,已成為軟體開發領域不可替代的經典。這本書解釋了重構的原理和最佳實踐方式,並給出了修改程式碼的動機和具體案例,值得反覆消化咀嚼。

這本書還凝聚了多位軟體開發領域專家的寶貴經驗:摩根大通架構師 Bill Opdyke 在第 13 章記述他將重構技術應用到商業開發過程中的一些問題;軟體開發方法學泰斗 Kent Beck 和 Don Roberts 合寫了第 14 章,展望重構技術的未來——自動化工具;Kent Beck 還寫了最後一章,總結如何學習重構。

你將從這本書中獲得:

理解什麼是重構、為什麼要重構、何時重構,理解

理解重構原則:一次一小步地修改程式碼並多次測試

實操演練重構的動機和方法,使既有程式碼更易理解、提升軟體的可維護性

無論你是軟體工程師還是產品經理,都需要翻一翻這本經典;而系統設計師和架構師則更有必要了解重構原理,根據需要在自己的專案中運用重構技術、最佳化系統性能。

小編提醒,這本書中第一版的案例語言使用 Java,第二版的語言使用 JavaScript。總體而言,作者展示的重構手法在各種主流的面嚮物件語言中基本上都可以通用。

為何重構?

第二章中,作者詳細介紹了重構的價值。重構不僅可以改進軟體設計本身的缺陷、幫助找到 bug、提升開發速度,還可以使軟體更容易被理解——這是因為,

程式設計很大程度上是人與計算機、人與人的溝通。

Martin Fowler 曾提及,任何一個傻瓜都能寫出計算機可以理解的程式碼,

唯有能寫出人類容易理解的程式碼的,才是優秀的程式設計師。

所謂程式設計,便是與計算機交談。你編寫程式碼告訴計算機做什麼事情,它的響應則是按照你的指示行動。你得及時填補

「想要它做什麼」和「告訴它做什麼」之間的縫隙

。這種程式設計模式的核心就是「準確說出我想要的」。除了計算機之外,你的原始碼還有其他讀者。計算機是否多花了幾個小時來編譯,又有什麼關係呢?如果一個程式設計師花費一週時間來修改某段程式碼,那才要命呢——如果他理解了你的程式碼,這個修改原本只需一小時。……而很多時候,那個未來的程式設計師就是我自己。

《重構(第2版)》譯者熊節也曾談到,

「程式設計其實是個社會活動」

一方面,程式設計師要把自然語言說出來的需求翻譯成機器能執行的機器語言;另一方面,翻譯出來的結果(也就是程式碼)還要支撐團隊(包括技術和非技術的團隊)不斷地在它基礎上協作和交流。……程式設計的大挑戰不是把程式碼寫出來,而是要在程式碼的基礎上建立有效的多方溝通。

那麼,我們

何時需要重構

?書中第三章列舉了一些「程式碼的壞氣味」。「壞氣味」指的是程式碼中某些不完美之處,開發人員可以透過這些細節上的徵兆在程式碼中追捕到更大問題。小編不禁聯想到了

《Clean Code》中的「好氣味」和「壞氣味」

一個重構案例

眾所周知,重構有風險,挖坑需謹慎。如果重構方式不恰當,風險反而更大。

(文末福利)如果程式碼莫名其妙跑起來了,就不要去動它了……嗎?

試想一下這樣的情況:你挖掘自己的程式碼,很快就發現了一些可以修改的地方,於是你挖得更深。挖得愈深,可以修改的地方就愈多……最後,你給自己挖了一個大坑,再也爬不出去了。

為了避免掉進坑裡,重構必須按照一定的原則和方法進行。

作者在第 5 - 12 章給出了一個重構列表,每一個重構案例都寫明瞭重構適用的情景、動機、重構方法。讓我們來看一個案例吧:

Extract Method(提煉函式)

你有一段程式碼可以被組織在一起並獨立出來:

void printOwing(double amount) { printBanner(); //print details System。out。println (“name:” + _name); System。out。println (“amount” + amount); }

將這段程式碼放進一個獨立函式中,並讓函式名稱解釋該函式的用途:

void printOwing(double amount) { printBanner(); printDetails(amount); } void printDetails (double amount) { System。out。println (“name:” + _name); System。out。println (“amount” + amount); }

這樣做的動機:

函式應該簡短而有的良好命名。

原因如下:

首先,如果每個函式的粒度都很小,那麼函式之間彼此複用的機會就更大;其次,這會使高層函式碼讀起來就像一系列註釋;再者,如果函式都是細粒度,那麼函式的覆寫也會更容易些。

如果提煉動作可以強化程式碼的清晰度,那就去做,就算函式名稱比提煉出來的程式碼還長也無所謂。

重構方法

創造一個新函式,以它「做什麼」來命名, 而不是以它「怎樣做」命名

將提煉出的程式碼從源函式複製到新建的目標函式中

仔細檢查提煉出的程式碼,看看其中是否引用了「作用域限於源函式」的變數(包括區域性變數和源函式引數)

檢查是否有「僅用於被提煉碼」的臨時變數,如果有,則在目標函式中將它們宣告為臨時變數

檢查被提煉碼,看看是否有任何區域性變數的值被它改變。如果一個臨時變數值被修改了,看看是否可以將被提煉碼處理為一個查詢,並將結果賦值給相關變數。如果很難這樣做,或如果被修改的變數不止一個,你就不能僅僅將這段程式碼原封不動地提煉出來。你可能需要先使用 Split Temporary Variable 方法,然後再嘗試提煉。也可以使用 Replace Temp with Query 將臨時變數消滅掉。

將被提煉碼中需要讀取的區域性變數,當作引數傳給目標函式

處理完所有區域性變數之後,進行編譯

在源函式中,將被提煉碼替換為「對目標函式的呼叫」

如果你將任何臨時變數移到目標函式中,請檢查它們原本的宣告式是否在被提煉碼的外圍。如果是,現在你可以刪除這些宣告式了

編譯,測試

隨後,作者給出了無區域性變數、有區域性變數、對區域性變數再賦值三種範例,手把手解釋如何提煉函式。

更多資源

這本書第 5 - 12 章是一份「重構列表」,有些概念比較抽象。為了更好地理解列表的內容,小編推薦

Refactoring Guru

這個網站,網站上不僅有許多關於重構的實際解釋,還介紹了常用的設計模式和設計原則。這個網站可以幫助你用簡單便捷的方式迅速掌握重構的理念、學習各個設計模式。

(文末福利)如果程式碼莫名其妙跑起來了,就不要去動它了……嗎?

想要了解更多詳細內容?關注 Zilliz 公眾號並回復書名

「重構」

即可獲得小編整理的《重構:改善既有程式碼的設計》高畫質思維導圖。

積累程式碼量很重要,

讀書、讀好書也很重要。

「Zilliz 好書推薦」欄目,

旨在與你分享技術成長相關的書籍,

與你一起先把書讀厚,再把書讀薄。

——————————————————————————

Zilliz 以重新定義資料科學為願景,致力於打造一家全球領先的開源技術創新公司,並透過開源和雲原生解決方案為企業解鎖非結構化資料的隱藏價值。

Zilliz 構建了 Milvus 向量資料庫,以加快下一代資料平臺的發展。Milvus 資料庫是 LF AI & Data 基金會的畢業專案,能夠管理大量非結構化資料集,在新藥發現、推薦系統、聊天機器人等方面具有廣泛的應用。