天天看點

重構筆記(一)——壞味道

第一章: 重構第一個案例

1. 重構第一步:建立一組可靠的測試環境

2. 提取出函數: 選中區域,點選Extract method(可修改參數名,函數名,扔出異常,加入comment,),運作測試判斷是否正确. 閱讀代碼時,我經常重構.這樣随着對程式的了解逐漸加深,也會不斷把這些了解嵌入代碼中.

3. 判斷子函數的參數,将參數隻屬于特定類的子函數複制到對應類,編譯并測試

4. 移出臨時變量,編譯并測試

5. 對于循環增加的臨時變量,extract method并将循環移入.重構時不必擔心性能,優化時才需要擔心.優化時可通過插入stopwatch确定性能點,進而優化

6. 對于預測變化時的重構,盡量減少受到影響的範圍

7. 對于switch語句,使用state pattern重構,是為了需求變化時更加容易

第二章: 重構原則

修改功能和重構,無論何時都應該清楚自己戴的是哪一頂帽子.

重構之前:代碼必須在大部分情況下能正常運作

何時重構

添加功能,修改錯誤

何時不該重構

項目進入最後期限

性能

程式設計時,不需要關注性能,當編寫完成後,使用測試軟體找到hot pot,然後針對該區域進行優化

重構的難題

資料庫

       通過在對象模型和資料庫模型之間插入一個隔離層

修改接口

       如果該接口使用者無法控制,在接口改變時,保留原接口,設為depreciated。舊接口調用新接口實作。

第三章: 代碼的壞味道

重複代碼:

同一類中的兩個函數:extract method

同一類的兩個子類:extract method, pullup method, form template method

做同一件事的兩個不同算法:使用一個清晰的取代其他,substitute algorithm

兩個不相幹的類:将共有代碼extract class ,另一個類調用,也需要從語義上判斷該函數是否屬于一個類

large function:

讓function容易了解的關鍵是起一個好名字

每當需要以注釋解釋函數時,就将要說明的東西寫入一個函數并以函數的用途來命名它,哪怕替換後的函數比原來還長,隻要函數名上看出其用途,也應該毫不猶豫地這樣做.

方法:

        99%的場合:Extract method

        太多的參數和臨時變量: replace temp with query和introduce parameter object

        臨時變量被多次複制:split temporary variable

該提煉哪些:

尋找注釋. 就算隻有一行代碼,如果其需要注釋,也應該将其提出到一個函數

條件: decompose conditional

循環:将循環和其内的代碼提煉到一獨立的函數

large clas

方法:

先找到彼此相關的變量(有相同字首或字尾);提煉該變量相關的代碼:extract class, move field, move method

太多參數

方法:

        參數由函數計算得到:replace parameter with method

        參數所在對象存在:preserve whole object

        參數的對象不存在:introduce parameter object

類的職責

多種原因的變化要修改一個類:extract class

一個變化修改多個類: move field,move method

依戀情節:兩個類耦合

調用某一類過多的取值函數:move method

函數的部分:先extract method,再move method

一個函數用到多個類:先extract method, 将該函數放到用到資料最多的類

Data clumps:資料泥團

删除資料的一項,其他資料不再有意義

方法:

        将這些字段 Extract class;

函數參數 introduce parameter object

不必在意隻用上字段的一部分字段。

Switch statements

單一函數中:replace parameter with explicit method

選擇條件之一是null: introduce  null object

大量使用:1)extract method将switch提取到獨立函數;2)move method到多個類;3)replace type code with subclass, replace type code with state/strategy

Parallel Inheritance Hierarchies 平行繼承體系

每當為一個類增加子類,需要為另一類應增加一個子類

方法:

        讓繼承體系的一個子類引用另一個體系的執行個體;move method ,move field;就可以消除引用端的繼承體系

Speculative Generality

如果函數或類的唯一使用者是測試用例

類:inline class 或者collapse hierarchy

函數的某些參數:remove parameter

函數名過于抽象:rename method

Message Chains

一長串的getThis()

觀察該函數具體的作用,将該函數放入消息鍊的正确位置

Middle Man:過度委托

某個類有一半的函數都委托給其他類

方法:

remove middle man;

              少數幾個函數:inlineMethod

Incomplete Library Class:不完美的庫類

修改一兩個函數:Introduce Forign Method

增加一大堆行為:Introduce Local Extension

Data Class

除了get set 什麼也不幹

找到調用處,move method; 對不提供修改的字段 remove set method;後期可用hide method隐藏get和set

Comments

說明參數規格: introduce assertion

合理的注釋:将來的打算,為什麼要這麼做

第四章構築測試體系

寫程式最花時間的是調試錯誤,找出錯誤比較費時,改正錯誤是很快的

作用:

       描寫該功能如何使用

       集中于接口而非實作