记录一下读《重构-改善代码既有设计》中作者对于重构代码所列举的手法清单。具体的流程细节不多说,书中很详细,这里主要做一个整理和我心中的理解。这个篇章的代码均为java。
1.ExtractMethod(提炼函数)
简单理解就是把冗长函数中的功能一一提炼成为一个新的函数。重构代码的目的之一是为了让代码更加容易理解,希望上层的代码读起来,就像读注释一样,这对函数的起名也得讲究,一般以“做什么”去起名,而不以“怎么做起名”。
注意的点就是一些局部或者临时变量的处理,这个我认为不需要多说,写代码熟练的人应该都会有意识的处理这些。
2.InlineMethod(内联函数)
double basePrice=anOrder.basePrice();
return (basePrice>1000)
----->
return (anOrder.basePrice()>1000)
使用情况一般是,某个临时变量是某个函数调用的返回值,当这个临时变量妨碍到其他重构的手法,就应该将它内联化。
3.Replace Temp with Query(以查询取代临时变量)
什么叫查询呢,作者其实用查询这个词会有点让人摸不着头脑。其实是当一个临时变量保存了一个表达式的结果,这时候把这个表达式运算放到一个独立的函数,函数返回这个表达式的结果,这样其他地方如果用到这个表达式的结果就可以在这个函数里去“查询”。
用书中的例子来看一下常见且较简单的使用情况:
double basePrice=_quantity*_itemPrice;
if(basePrice>1000)
return basePrice*0.95;
else
return basePrice*0.98;
---->
if(basePrice()>1000)
return basePrice()*0.95;
else
return basePrice()*0.98;
double basePrice()
{
return _quantity*_itemPrice;
}
首先就将所有引用到这个临时变量的地方全部替换为从函数的调用(从函数中查询)。Replace Temp with Query往往是Extract Method之前必不可少的步骤,因为临时变量会阻碍到我们提炼函数,所以要尽可能的把它们变为查询式。但是这种手法复杂的情况在于临时变量受到其他条件的影响。
再看一个例子:
double getPrice()
{
int basePrice=_quantity*_itemPrice;
double discountFactor;
if(basePrice>1000)
discountFactor=0.95f;
else
discountFactor=0.98f;
return basePrice*discountFactor;
}
---->替换basePrice
double getPrice()
{
double discountFactor;
if(basePrice()>1000)
discountFactor=0.95;
else
discountFactor=0.98;
return basePrice()*discountFactor;
}
private int basePrice()
{
return _quantity*_itemPrice;
}
---->替换 discountFactor
double getPrice()
{
double discountFactor=discountFactor();
return basePrice()*discountFactor;
}
private double discountFactor()
{
if(basePrice()>1000)
return 0.95;
else
return 0.98;
}
private int basePrice()
{
return _quantity*_itemPrice;
}
---->最后使用Inline Temp
double getPrice()
{
return basePrice()*discountFactor();
}
4.Introduce Explaining Variable(引入解释性变量)
这个很常见,我们在开发过程中经常会看到很臭长臭长的表达式,这时候我们可以把其中的部分拆分提出来用临时变量保存,这样会有助于我们阅读。然后再配合Extract Method和Replace Temp with Query来使用。
5.Split Temporary Variable(分解临时变量)
如图,是书中的一个例子。像acc这个临时变量,就被赋值了两次,承担了两种职责,这时候应该重新定义一个新的变量来执行第二个职责,然后再使用上述的一些方法来重构。
6.Replace Method with Method Object(以函数对象取代函数)
因为局部变量的存在会增加函数分解的难度,如果一个函数局部变量泛滥,那么是很难分解这个函数。Replace Temp With Query可以帮助减轻一些负担,但是有时候却根本无法拆解一个需要拆解的函数。这时候此方法就登场了。直接用书中的例子来解释下它的用法,可能这个例子不那么合适,毕竟这种情况一般需要很长篇幅的例子。
Class Account
int gamma (int inputVal,int quantity,int yearToDate)
{
int importantValue1=(intputVal*quantity)+delta();
int importantValue2=(inputVal* yearToDate)+100;
if((yearToDate - importantValue1)>100)
importantValue2-=20;
int importantValue3=importantValue2*7;
..
return importantValue3-2*importantValue1;
}
---->
//这里把函数变成一个函数对象,需要声明一个新的类
class Gamma
private 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;
intputVal=inputValArg;
quantity=quantityArg;
yearToDate=yearToDateArg;
}
//把原本的函数内容移到Compute,里边引用到Account中成员的部分都替换好
int compute()
{
int importantValue1=(intputVal*quantity) + _account.delta();
int importantValue2=(inputVal* yearToDate)+100;
if((yearToDate - importantValue1)>100)
importantValue2-=20;
int importantValue3=importantValue2*7;
..
return importantValue3-2*importantValue1;
}
class Account
//这里就把功能交给Gamma对象
int gamma(int inputVal,int quantity,int yearToDate)
{
return new Gamma(this,intputVal,quantity,yearToDate).compute();
}
这样的好处就在于可以轻松的对Compute里面的内容采用Extract Method,不用再担心参数传递的问题。因为参数早已变成了字段。
7.Substitute Algorithm(替换算法)
这个就不必说了,一定可以在某些时候找到更简单的算法。