重構的方式和流程
- 兩頂帽子:開發的時候,開發人員經常在兩種狀態間切換,或者帶着重構的帽子重構代碼,或者帶着開發的帽子新增代碼
- 小步前進:由於重構不改變現有代碼的邏輯,所以對於一個大功能,可以先重構一部分,測試,再接着重構一部分
- 做好測試:重構都是修改現有代碼,那麼應當做到,以前能跑通的測試,重構後依舊可以跑通。這也是說,重構沒有問題的一個表現是,通過所有預期的測試。
代碼的壞味道(何時重構)
- 重複代碼、異曲同工的類
- 過長函數:要寫註釋的地方,不妨分解一個函數,用函數名說明意圖
- 過大的類
- 過長的參數列表:可以在函數內部通過調用其他函數得到的參數,可以不用通過入參得到
- 發散式變化、霰彈式修改:每次變化都要修改多個函數的時候,不妨將修改的地方提取到一處或者一個類中
- 依戀情節、狎暱關係:一個類對另一個類的數據和行爲,比對自身更感興趣,這時說明,類的功能劃分有誤
- 數據泥團:幾項數據,通常一起出現,一起被需要,那麼,他們可能需要結成對象
- 基本類型偏執:也許,使用小型對象,可以表示更多東西
- switch語句:switch語句有時意味着,可以使用面向對象的繼承和多態來避免
- 面向未來編程、暫時性字段…:當前不用的字段和僅在一定情況下使用的字段,請謹慎編寫
- 中間類:當一個類基本把所有操作全轉接給其他類處理時,這個類的作用就有待商榷,也許可以去掉
- 被拒絕的遺贈:父類的全部方法,子類都應該繼承,而不是選擇一部分繼承,如果是這樣,可能父類需要把某些方法下沉
重構手法
重新組織函數
- 提煉函數/內聯函數
- 引入解釋性變量保存複雜表達式結果
- 將不同用途的臨時變量區分開
- 使用函數對象取代函數:對於一個大型函數內,因爲臨時變量的緣故,函數無法拆解的情況,可以抽取函數對象,即保存變量,又有拆解的函數
對象之間搬移特性
- 搬移函數
- 搬移字段
- 提煉類/內聯類
- 隱藏委託關係/移除中間類
- 引入外加函數/引入本地擴展(子類或者包裝類)
重新組織數據
- 自封裝字段:必要時再進行
- 以對象取代數據值
- 封裝字段
- 封裝集合(返回不可修改的集合)
- 以類取代類型碼:當類型碼不影響行爲的時候,爲了運行時檢測安全,可以使用類取代類型碼
- 以子類取代類型碼:當類型碼影響行爲的時候,可以使用子類替代類型碼,藉助多態的優點,讓與特定類型有關的代碼下沉,這裏對類型的取值函數,可以借用自封裝字段
- 用狀態模式/策略模式取代類型碼:當類型碼在對象存活期間會變化時
- 用類型碼替代子類:當不同之處只在返回的類型碼時
簡化條件表達式
- 以衛語句取代嵌套條件表達式:就是將不合法的判斷前置,提前return
- 以多態取代條件判斷:首先,應當使用以子類/狀態模式/策略模式替代類型碼構建了繼承體系,然後將條件判斷的語句上移爲抽象函數,由子類各自實現
- 引入null對象:在返回null的時候,考慮可否返回一個“空對象”,以避免反覆的null校驗
簡化函數調用
- 移除參數:對於多態函數來說,複雜一些,需要考察是否每一個子類的實現都不需要這個參數
- 令函數攜帶參數/以明確函數名取代參數
- 以函數取代參數:函數入參可以通過調用其他參數即時獲取,則無需入參
- 引入參數對象:爲了抵抗前文提到的“數據泥團”
- 以工廠函數取代構造函數
- 以異常取代錯誤碼
- 以測試取代異常
處理概括關係
- 字段上移/下移
- 函數上移/下移
- 構造函數上移
- 摺疊繼承體系:把子類融入父類
- 塑造模版函數:某些函數以相同順序執行類似操作,將類似操作抽象相同簽名,則某些函數的調用可以合併爲一種,將其上移至超類
- 以繼承取代委託/以委託取代繼承:子類可能只需要父類的一部分功能,這時相對於繼承,組合(委託)更爲合適;反之,可以使用繼承
個人感覺很有用的重構手法
- 使用函數對象取代函數
- 自封裝字段
- 以子類取代類型碼
- 引入null對象
- 以衛語句取代嵌套條件表達式
- 以異常取代錯誤碼