Move Method (搬移函數)

Summary: 

程序中,有個函數與其所駐類以外的另個一個函數進行更多的交流:調用後者,或者被後者調用。 java

 在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是將舊函數徹底移除。 函數

Motivation:

「搬移函數」是重構理論的支柱。若是有一個類有太多行爲,或若是一個類與另外一個類有太多合做而造成高度耦合,就能夠辦以函數。經過這種手段,可使系統中的類更簡單,這些類最終也將更乾淨利落地實現系統交付的任務。 測試

Mechanics:

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中的另外一個函數,就不能這麼簡單的處理。這種狀況下必須將源對象傳遞給目標函數。

若是須要源類的多個特性,那麼咱們也能夠將源對象傳遞給目標函數。不過若是目標函數須要太多源類特性,就得進一步重構。一般這種狀況下,咱們須要分解目標函數,並將其中一部分移回源類。

相關文章
相關標籤/搜索