
Any fool can write code that a computer can understand. Good Programmers write code that humans can understand. — M. Fowler (1999)
Martin Fowler 在第二版書封上開宗明義地寫出他對於好的工程師的定義,而重構(refactoring)是成為好的工程師所必經的途徑。隨著軟體規格需求的演進,原本認為好的程式碼,都有可能漸漸變得不合時宜,不論是 junior 或是 senior 的工程師,重構程式碼都是一項不可或缺、必須被不斷執行的技藝。
怎麼樣才算是重構(refactoring)呢?作者的定義如下:
Refactoring is the process of changing a software system in a way that does not alter the external behavior of the code yet improves its internal structure.
本書主要分成兩個部分,前半部分(1~4章)著重在重構的原則與心法,後半部分(5~12章)則專注在實踐各種重構的動機與技巧。在第二版中,作者選擇了 JavaScript 做為範例的程式語言(第一版是用 Java),因為作者認為 JavaScript 是一個大部分工程師都能讀懂的語言,而語言本身其實並不會影響他所要傳達的內容,我認為作者在本書的兩個版本中,分別使用了不同語言實作,就是一個最好的證明。
我認為本書最精彩的地方在前半講述重構原則與心法的部分,作者點出了許多讓我受用不盡的原則,像是:
- First refactor the program to make it easy to add the feature, then add the feature.
- Before you start refactoring, make sure you have a solid suite of tests.These tests must be self-checking.
- Always leave the code base healthier than when you found it.
- Three strikes, then you refactor.
- The whole purpose of refactoring is to make us program faster, producing more value with less effort.
在重構的定義中,我們了解到重構前後必須保持程式碼的外在行為,這讓自動化測試在重構的過程中扮演了非常重要的角色,作者在上述第二項原則中,更強調了在開始重構前,都必須要有測試覆蓋即將被更改的程式碼區塊。沒有測試保護,我們會害怕破壞原有功能而不敢輕易著手重構(尤其是 legacy code);或是先改了再說,然後程式就遍地開花,然後就踏上了一個回不了頭、沒有盡頭的 debug 旅程。測試不只提供了功能的驗證,還能除祛改動程式碼的畏懼心理,每一個重構步驟完成後,都該立即執行測試驗證,將錯誤範圍縮到最小,這也是作者在書中不斷傳達的重構節奏。
我曾經有一個 class A ,做了很多不是 A 應該關注的事情,在有測試保護 A 的狀況下,我將許多 A 的程式碼抽離至 class B,讓 A 去依賴 B 的介面,並在搬運程式碼的過程中不斷地執行測試驗證 A 對外揭露的功能,很有信心地在不改變 A 的行為下完成了這一系列的重構動作。這次重構完成不久後,偶然在其他的地方發現也需要使用到 class B 的功能,原來對於 class A 的重構不僅僅使 A 的職責變得單純,抽離出來的 class B 還能被其他地方繼續重用,無形中提升了許多開發的速度,讓我深刻體會到了上述第五項原則所闡述的道理:重構讓我們用更少的力氣來達成更快速的開發。
作者還有另外一個非常受用的建議:
When you feel the need to write a comment, first try to refactor the code so that any comment becomes superfluous.
因為這條原則,讓我現在的程式碼裡沒有多餘的註解:
這是一段使用 TypeScript 處理登入並發放 Token 的函式,在函式裡透過子函式的名稱來表達每一行程式的意圖,在不需要註解的情況下就能簡潔明瞭地描述出處理登入要做的事情,當然這個函式最一開始並不是長這個樣子,在測試的保護下,中間其實經過了許多次的重構與提煉。但這並不代表註解就是不好的,更多關於註解的討論可以參考這篇文章:
不管好的或壞的程式都需要被持續地重構,軟體之所以被稱為軟體,是因為它隨時都可能會變動,當下好的決定,都可能在未來變成累贅,唯有以變應變,才能保持穩定的開發速度以及步調。