主要針對於Long Methods 算法
主要重構手法:Extract Method 函數
Tips: 若是在進行屢次提煉以後,意識到提煉所得的某些函數並無作任何實質事情,或若是須要回溯回覆原先函數,就須要Inline Method(將一個函數調用動做替換爲該函數本體) 測試
Extract Method 最大的困難就是處理局部變量,而臨時變量則是其中一個主要的困難源頭。 spa
處理一個函數時,能夠運用 Replace Temp with Query去掉全部可去掉的臨時變量。若是不少地方使用了某個臨時變量,先運用Split Temporary Variable將它變得比較容易替換 對象
若是臨時變量實在太混亂,難以替換。這時候就須要使用Replace Method with Method Object , 它能夠分解哪怕最混亂的函數,代價則是引入一個新的類。 繼承
若是在函數內給參數進行了賦值,那麼得使用Remove Assignments to Parameters 遞歸
函數分解完畢後,若是發現算法能夠改進從而使代碼更清晰,則可使用Substitute Algorithm 引入更清晰的算法。 ip
1. Extract Method (提煉函數)
Summary:
將一段代碼放進一個獨立函數中,並讓函數名稱解釋該函數的用途 it
Motivation:
- 若是每一個函數的粒度都很小,那麼函數被複用的機會就更大
- 這會使高成函數讀起來就像一系列註釋
- 若是函數都是細粒度,那麼函數的複寫也會更容易些
Mechanics:
- 建立一個新函數,根據這個函數的意圖來對它命名(「作什麼」而非「怎樣作」)
- 將提煉出的代碼從原函數複製到新建的目標函數中
- 仔細檢查提煉出的代碼,看看其中是否引用了「做用侷限於原函數」的變量(包括局部變量和原函數參數)。
- 檢查是否有「僅用於被提煉代碼段」的臨時變量。若是有,在目標函數中將它們聲明爲臨時變量。
- 檢查被提煉代碼段,看看是否有任何局部變量的值被它改變。若是一個臨時變量值被修改了,看看是否能夠將被提煉代碼段處理爲一個查詢,並將結果賦值給相關變量。若是很難這樣作,或若是被修改的變量不止一個,你就不能僅僅將這段代碼原封不動地提煉出來。你可能須要先使用Split Temporary Variable, 而後嘗試提煉。也可使用Replace Temp with Query 將臨時變量消滅掉。
- 將被提煉代碼段中須要讀取的局部變量,當作參數傳給目標函數。
- 處理完全部局部變量後,進行編譯。
- 在原函數中,將被提煉代碼段替換爲對目標函數的調用。
- 編譯,測試。
2. Inline Method (內聯函數)
Summary:
在函數調用點插入函數本體,而後移除該函數。 io
Motivation:
- 函數內部代碼和函數名稱一樣清晰易讀,去掉非必要的間接性。
- 有一羣組織不甚合理的函數,能夠將它們都內聯到一個大型函數中,再從中提煉出組織合理的小型函數。實施Replace Method with Method Object以前先這麼作,每每能夠得到不錯的效果。
- 若是別人使用的太多間接層,使得系統中的全部函數都彷佛只是對另外一個函數的簡單委託,形成繁瑣的委託動做,這時可使用Inline Method。試着使用內聯手法,找出有用的間接層,移除無用的間接層。
Mechanics:
- 檢查函數,肯定它不具多態性。(若是子類繼承了這個函數,就不要將此函數內聯,由於子類沒法覆寫一個根本不存在的函數。)
- 找出這個函數的全部被調用點。
- 將這個函數的全部被調用點都替換爲函數本體
- 編譯,測試。
- 刪除該函數的定義。
Tips: 對於遞歸調用,多返回點,內聯至另外一個對象中而該對象並沒有提供訪問函數等複雜狀況,那麼就不該該使用這個重構手法
3. Inline Temp(內聯臨時變量)
Summary:
將全部對該變量的引用動做,替換爲對他賦值的那個表達式自身。
Motivation:
Inline Temp多半是做爲Replace Temp with Query的一部分使用的,因此真正的動機出如今後者那兒。惟一單獨使用Inline Temp的狀況是:你發現某個臨時變量被賦予某個函數調用的返回值。通常來講,這樣的臨時變量不會有任何危害,能夠放心地把它留在那兒。但若是這個臨時變量妨礙了其餘的重構手法,例如Extract Method,就應該將它內聯化。
Mechanics:
- 檢查給臨時變量賦值的語句,確保等號右邊的表達式沒有反作用。
- 若是這個臨時變量並未被聲明爲final,那就將它聲明爲final,而後編譯。(這能夠檢查該臨時變量是否真的只被賦值一次)
- 找到該臨時變量的全部引用點,將它們替換爲「爲臨時變量賦值」的表達式。
- 每次修改後,編譯並測試。
- 修改完全部引用點以後,刪除該臨時變量的聲明和賦值語句。
- 編譯,測試。
4. Replace Temp with Query(以查詢取代臨時變量)
Summary:
將這個表達式提煉到一個獨立的函數中。將這個臨時變量的全部引用點替換爲對新函數的調用。此後新函數就可被其餘函數使用。
Motivation:
臨時變量的問題在於:它們是暫時的,並且只能在所屬函數內使用。因爲臨時變量只能在所屬函數內可見,因此它們會驅使你寫出更長的函數,由於只有這樣你才能訪問到須要的臨時變量。若是把臨時變量替換爲一個查詢,那麼同一個類中的全部函數都將能夠得到這份信息。這將帶給你極大幫助,使你可以爲這個類編寫更清晰的代碼。
Replace Temp with Query 每每是運用Extract Method以前必不可少的一個步驟。局部變量會使代碼難以被提煉,因此應該儘量把它們替換爲查詢式。
這種重構手法較爲簡單的狀況是:臨時變量只被賦值一次,或者賦值給臨時變量的表達式不受其餘條件影響。其餘狀況比較棘手,但也有可能發生。可能須要先運用Split Temporary Variable 或Separate Query from Modifier 視狀況變得簡單一些,而後再替換臨時變量。若是你想替換的臨時變量是用來收集結果的(例如循環中的累加值),就須要將某些程序邏輯(如循環)複製到查詢函數去。
Mechanics:
- 找出只被賦值一次的臨時變量。(若是某個臨時變量被賦值超過一次,考慮使用 Split Temporary Variable)將它分割成多個變量
- 將該臨時變量聲明爲final(這可確保臨時變量的確只被賦值一次)
- 編譯
- 將「對該臨時變量賦值」之語句的等號右側部分提煉到一個獨立函數中。(首先將函數聲明爲private。往後你可能會發現有更多類須要使用它,那時放鬆對它的保護也容易。其次,確保提煉出來的函數無任何反作用,也就是說該函數並不修改任何對象內容。若是它有反作用,就對它進行Separate Query from Modifier)
- 編譯,測試
- 在該臨時變量身上實施Inline Temp。