重構手法-從新組織函數

 主要針對於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。
相關文章
相關標籤/搜索