《重構——改善既有程式碼的設計》感想

WoodMap發表於2020-04-04

為什麼會拾起這本書來研讀呢?一方面是因為近期在專案的更換中發現兩個問題

  1. 讀懂別人的程式碼不易,特別是在工程複雜的時候
  2. 自己寫的程式碼能否讓別人輕易讀懂,包括後續的維護是否簡易、擴充套件性是否足夠。
  3. 寫出讓機器讀懂的程式碼很正常,寫出讓別人輕易讀懂的程式碼,才是優秀的。

手頭上剛好有這麼本書,希望能夠在閱讀和做總結的過程中,能不斷嚴厲要求和規範自己,讓自己的程式碼賞心悅目。得到近期荒廢之後另一個進步。PS,估計目前只簡記心得,後續如果有時間可以另外嘗試分享現實經驗。

《重構——改善既有程式碼的設計》(第二版)

前情,重構程式碼的一些抽象關鍵點

  • 重構程式碼的步驟
  1. 確保有可靠的測試集,保證自己每一處修改可編譯且不會引起新增問題
  2. 每一步都是粒度最小的改動,commit本地,一旦發現問題,可以最快的找到最後的改動點,避免在多步驟的改動後無法快速定位引起問題的改動點。
  3. 拆分,注意命名、變數、函式、多型。
  • 重構不應該使原有功能面目全非,而是結構化調整。
  • 兩頂帽子 —— 新增新功能 & 重構,兩者獨立,但可以經常性交替。經常性地對程式碼進行重構,可以保證程式碼框架足夠清晰,不會因為時間堆積而讓程式碼越來越讓人看不懂。
  • 只要想重構,就需要有一套可以自測試的程式碼,來快速發現錯誤、避免錯誤。
  • 重構有效的關鍵及協同效應。並且需要自我判斷,是否需要重構。
  1. 自測試程式碼
  2. 持續整合
  3. 重構
  • 重構影響效能,那麼就關注在影響效能熱點的小部分程式碼上(10%),讓他發揮最大效用。

如何重構程式碼,什麼程式碼需要重構

  • 命名不規範
  • 重複的程式碼
  • 過長的函式
  • 過長的引數列表 —— 引數的減少
  • 全域性變數引起的莫名bug & 可變資料 —— 封裝變數
  • Fancy Evny —— 策略模式和訪問者模式
  • 這裡居然講了24種,包括最後的註釋,但慢慢地,看到中間部分,發現其實簡單的重構方法,自己在平時專案中也有形成這種習慣了。

養成自測試的習慣

  • 應該在開始編寫模組的時候就寫自測試程式碼,後續當你改動時,就可以通過這些程式碼保證改動的有效和準確
  • 正常路徑的測試,邊界路徑的條件也需要考慮(這個其實在自己的實踐中經常被故意忽略,後續要養好習慣才行,如果你注意到邊界行為,那麼就需要去驗證。)

一些重構的方法(作者的名錄),從動機和做法出發

  • 提煉函式
  • 行內函數
  • 提煉變數(這種在使用表示式較長的時候,可以提煉變數,使程式碼更容易閱讀)
  • 內聯變數(當變數本身足夠清楚表達含義時,就不需要提煉變數了)
  • 改變函式宣告
  • 封裝變數
  • 引入引數物件(當多個函式使用了相同的引數時)
  • 函式組成類(共同的函式作用範圍,可以簡單理解為當一些方法是有相同的共性的時候,比如提取一些工具類的方法)
  • 函式組合成變換(需要派生資訊的時候,enrich something, 當你的輸入會得到一個變化的輸出(同個物件,變化了)
  • 拆分階段

封裝

  • 封裝記錄,封裝變數、封裝到函式或者類中
  • 以物件取代基本型別
  • 新增中間人 & 移除中間人 (委託關係是否必要)
  • 替換演算法

搬移特性

  • 搬移函式、搬移變數
  • 搬移語句到函式中 <--> 搬移語句到呼叫者中
  • 以函式呼叫取代內聯程式碼
  • 合併重複語句
  • 拆分迴圈(讓一個迴圈只做一件事情)
  • 以管道取代迴圈(Replace Loop with Pipeline)【這個Colletion PipeLine的概念比較少使用到,後面關注】
  • 移除死程式碼(如果是團隊協同,那麼就指操作自己部分的程式碼吧!想起了還沒畢業的時候,第一次寫一個業務需求,將別人的程式碼改動得亂糟糟,哈哈哈哈哈)

重新組織資料

  • 拆分變數(移除對變數的賦值)
  • 欄位改名
  • 以查詢取代派生變數(Replace Derived Variable with Query)
  • 將引用物件改為值物件 <--> 將值物件改為引用物件

簡化條件邏輯

  • 分解條件表示式
  • 合併條件表示式
  • 以衛語句取代巢狀條件表示式(Replace Nested Conditional with Guard Clauses)
  • 以多型取代條件表示式
  • 引入特例
  • 引入斷言(在目前的實際專案中,很少在應用層使用斷言,一般在單元測試中使用)

重構API

  • 將查詢函式和修改函式分離(任何有返回值的函式都應該沒有副作用)
  • 函式引數化
  • 移除標記引數(感覺是func test(A,B) -- > setA(B)
  • 保持物件完整
  • 以引數代替查詢 -- 以查詢代替引數
  • 移除設定函式(如果不希望引數可以被改變,那就去掉setter吧)
  • 以工廠函式取代建構函式
  • 以命令取代函式(以函式物件取代函式) -- 以函式取代命令

處理繼承關係

  • 函式上移(下移)(子類中有相同的方法,可以移動到父類中)
  • 欄位上移(下移)
  • 建構函式上移(下移)
  • 以子類代替型別碼(移除子類)
  • 提煉超類 Extract Superclass
  • 摺疊繼承體系(當子類和超類無太大區別)
  • 以委託取代子類
  • 以委託取代超類

其實好像是本書的目錄結構,加上了一些自己的理解,也方便自己檢視 (有些翻譯很生澀,後續有空再仔細解說) 在記錄總結的過程中,發現其實很多習慣已經在實際專案中使用且注意到了,欣慰。當然後面得更好地讓自己養成習慣才好。

以上。

相關文章