Summary:
有一个大型函数,其中对局部变量的使用使你无法采用Extract Method。将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段。然后可以在同一个对象中将这个大型函数分解为多个小型函数。
Motivation:
局部变量的存在会增加函数分解的难度。如果一个函数之中局部变量泛滥成灾,那么想分解这个函数是非常困难的。Replace Temp with Query可以帮助减轻这一负担,但是有时候根本无法拆解一个需要拆解的函数。这种情况下,就要用到函数对象(method object)这一法宝。
Replace Method with Method Object 会将所有局部变量都变成函数对象的字段然后可以对这个新对象使用Extract Method 创造出新函数,从而将原本的大型函数拆解变短。
Mechanics:
1. 建立一个新类,根据待处理函数的用途,为这个类命名
2. 在新类中建立一个final字段,用以保存原先大型函数所在的对象。我们将这个字段称为“源对象”。同时,针对原函数的每个临时变量和每个参数,在新类中建立一个对应的字段保存之。
3.在新类中建立一个构造函数,接受源对象及原函数的所有参数作为参数。
4.在新类中建立一个compute () 函数。
5. 将原函数的代码复制到compute () 函数中。如果需要调用源对象的任何函数,请通过源对象字段调用。
6. 编译
7. 将旧函数的函数本体替换为这样一条语句:“创建上述新类的一个新对象,而后调用其中的compute () 函数”。
范例
public 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;
}
}
为了把这个函数变成一个函数对象,我们需要首先声明一个新类。在此新类中我们应该提供一个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类的地方,我们都必须改而使用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;
}
然后,修改旧函数,让它将工作委托给刚完成的这个函数对象。
int gamma( int inputVal, int quantity, int yearToDate )
{
return new Gamma(this, inputVal, quantity, yearToDate).compute();
}
这就是本项重构的基本原则。它带来的好处是:现在我们呢可以轻松地对compute()函数采取Extract Method, 不必担心参数传递的问题。