天天看點

Replace Method with Method Object(以函數對象取代函數)

class Order...

    double price() {

       double primaryBasePrice;

       double secondaryBasePrice;

       double tertiaryBasePrice;

       //   long computation;

      ...

    }

動機

我在本書中不斷向讀者強調小型函數的優美動人。隻要将相對獨立的代碼從大型函數中提煉出來,就可以大大提高代碼的可讀性。

但是,局部變量的存在會增加函數分解難度。如果一個函數之中局部變量泛濫成災,那麼想分解這個函數是非常困難的。Replace Temp with Query(120)可以助你減輕這一負擔,但有時候你會發現根本無法拆解的函數。這種情況下,你應該把手深深地伸入你的工具箱(好酒沉甕底呢),祭出函數對象(method object)這件法寶。

Replace Method with Method Object(135)會将所有局部變量都變成函數對象(method object)的值域(field)。然後你就可以對這個新對象使用Extract Method(110)創造出新函數,進而将原本的大型函數拆解變短。

作法:

1 建立一個新class,根據[待被處理之函數]的用途,為這個class命名。

2 在新class中建立一個final值域,用以儲存原先大型函數所駐對象。我們将這個值域稱為[源對象]。同時,針對原(舊)函數的每個臨時變量和每個參數,在新class中建立一個個對應的值域儲存之。

3 在新class中建立一個構造函數(constructor),接收源對象及原函數的所有參數作為參數。

4 在新class中建立一個compute()函數。

5 将原(舊)函數的代碼拷貝到compute()函數中。如果需要調用源對象的任何函數,請以[源對象]值域調用。

6 編譯。

7 将舊函數的函數本體替換為這樣一條語句:[建立上述新class的一個新對象,而後調用其中的compute()函數]。

class Account...

    int gamma(int inputVal, int quantity, int yearToDate) {

       int importantValue1 = (inputVal * quantity) + delta();

       int importantValue2 = (inputVal * yearToDate) + 100;

       if((yearToDate - importantValue1) > 100)

          importantValue2 -= 20;

       int importantValue3 = importantValue2 * 7;

       //   and so on.

       return importantValue3 -2 * importantValue1;

    }

為了把這個函數變成一個函數對象(method object),我首先需要聲明一個新class。在此新class中我應該提供一個final值域用以儲存原先對象(源對象):對于函數的每一個參數和每一個臨時變量,也以一個個值域逐一儲存。

class Gamma...

    private final Account _account;

    private int inputVal;

    private int quantity;

    private int yearToDate;

    private int importantValue1;

    private int importantValue2;

    private int importantValue3;

接下來,加入一個構造函數:

Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) {

    _account = source;

    inputVal = inputValArg;

    quantity = quantityArg;

    yearToDate = yearToDateArg;

}

現在可以把原來的函數搬到compute()了。函數中任何調用Account class的地方,我都必須改而使用_account值域:

int compute() {

       int importantValue1 = (inputVal * quantity) + _account.delta();

       int importantValue2 = (inputVal * yearToDate) + 100;

       if((yearToDate - importantValue1) > 100)

          importantValue2 -= 20;

       int importantValue3 = importantValue2 * 7;

       //   and so on.

       return importantValue3 -2 * importantValue1;

}

然後,我修改舊函數,讓它将它的工作轉發給剛完成的這個函數對象(method object):

int gamma(int inputVal, int quantity, int yearToDate) {

    return new Gamma(this, inputVal, quantity, yearToDate).compute();

}

這就是本項重構的基本原則。它帶來的好處是:現在我可以輕松地對compute()函數采取Extract Method(110),不必擔心引數(argument)傳遞。

int compute() {

    int importantValue1 = (inputVal * quantity) + _account.delta();

       int importantValue2 = (inputVal * yearToDate) + 100;

       importantThing();

       int importantValue3 = importantValue2 * 7;

       //   and so on.

       return importantValue3 -2 * importantValue1;

}

void importantThing() {

    if((yearToDate - importantValue1) > 100)

       importantValue2 -= 20;

}

繼續閱讀