淘先锋技术网

首页 1 2 3 4 5 6 7

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;
}