程序中,有個函數與其所駐類以外的另個一個函數進行更多的交流:調用後者,或者被後者調用。 java
在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是將舊函數徹底移除。 函數
「搬移函數」是重構理論的支柱。若是有一個類有太多行爲,或若是一個類與另外一個類有太多合做而造成高度耦合,就能夠辦以函數。經過這種手段,可使系統中的類更簡單,這些類最終也將更乾淨利落地實現系統交付的任務。 測試
1. 檢查源類中被源函數所使用的一切特性(包括字段和函數),考慮它們是否也該被搬移。 spa
若是某個特性只被打算搬移的那個函數用到,就應該將它一併搬移。若是另有其餘函數使用了這個特性,能夠考慮將使用該特性的全部函數全都一併搬移。有時候,搬移一組函數比逐一搬移簡單些。 code
2.檢查源類的子類和超類,看看是否有該函數的其餘聲明。 對象
若是出現其餘聲明,或許沒法進行搬移,除非目標類也一樣表現出多態性。 rem
3.在目標類中聲明這個函數。 io
能夠爲此函數選擇一個新名稱—對目標類更有意義的名稱 編譯
4. 將源函數的代碼複製到目標函數中。調整後者,使其能在新類中正常運行 class
若是目標函數使用了源類中的特性,咱們得決定如何從目標函數引用源對象。若是目標類中沒有相應的引用機制,就得把源對象的引用當作參數,傳給新創建的目標函數。
若是源函數包含異常處理,咱們得判斷邏輯上應該由哪一個類來處理這一異常。若是應該由源類來負責,就把異常處理留在原地。
5. 編譯目標類
6. 決定如何從源函數正確引用目標對象。
可能會有一個現成的字段或函數幫助取得目標對象。若是沒有,就看可否輕鬆創建一個這樣的函數。若是仍是不行,就得在源類中新建一個字段來保存目標對象。這多是一個永久性修改,但也可讓它是暫時的,由於後繼的其餘重構項目可能會把這個新建字段去掉。
7.修改源函數,使之成爲一個純委託函數
8.編譯,測試
9. 決定是否刪除源函數,或將它看成一個委託函數保留下來
若是要常常在源對象中引用目標函數,那麼將源函數做爲委託函數保留下來會比較簡單。
10.若是要移除源函數,請將源類中對源函數的全部調用,替換爲對目標函數的調用
11. 編譯,測試。
咱們用一個表示帳戶的Account類來講明這項重構
public class Account { private AccountType type; private int daysOverdrawn; double overdraftCharge() { if( type.isPremium() ) { double result = 10; if( daysOverdrawn > 7 ) { result += ( daysOverdrawn - 7 ) * 0.85; return result; } } else { return daysOverdrawn * 1.75; } return 0; } double bankCharge(){ double result = 4.5; if( daysOverdrawn > 0 ) { result += overdraftCharge(); } return result; } }假設有幾種新帳戶,每一種都有本身的「透支金額計費規則」。因此咱們但願將overdraftCharge() 搬移到AccountType 類去。
第一步要作的是:觀察被overdraftCharge()使用的每一項特性,考慮是否值得將它們與overdraftCharge() 一塊兒移動。此例中,咱們須要讓daysOverdrawn字段留在Account類,由於這個值不會隨不一樣種類的帳戶而變化。而後,咱們將overdraftCharge()函數複製到AccountType中,並作相應調整。
public class AccountType { double overdraftCharge( int daysOverdrawn ) { if( isPremium() ) { double result = 10; if( daysOverdrawn > 7 ) { result += ( daysOverdrawn - 7 ) * 0.85; return result; } } else { return daysOverdrawn * 1.75; } return 0; } }調整目標函數使之經過編譯,然後就能夠將源函數的函數本體替換爲一個簡單的委託動做,而後編譯測試。
public class Account { private AccountType type; private int daysOverdrawn; double overdraftCharge() { return type.overdraftCharge( daysOverdrawn ); } ... }咱們能夠保留代碼如今的樣子,也能夠刪除源函數。若是決定刪除,就得找出源函數的全部調用者,並將這些調用從新定向,改成調用Account的overdraftCharge().
在全部的調用點都修改完畢後,就能夠刪除源函數在Account中的聲明瞭。若是被搬移的函數不是private的,還要檢查其餘類是不是用了這個函數。
此例中被搬移的函數只引用了一個字段,因此只須要將這個字段做爲參數傳給目標函數就好了。若是被搬移的函數調用了Account中的另外一個函數,就不能這麼簡單的處理。這種狀況下必須將源對象傳遞給目標函數。
若是須要源類的多個特性,那麼咱們也能夠將源對象傳遞給目標函數。不過若是目標函數須要太多源類特性,就得進一步重構。一般這種狀況下,咱們須要分解目標函數,並將其中一部分移回源類。