场景演义
场景一 函数过长,难以理解,不知道做什么,如何做
重构手法
Extract Method(抽取公共代码)
场景二 函数内有大量参数与临时变量
重构手法
Replace Temp with Query(以查询取代临时变量)
程序以一个临时变量保存某一表达式的运行结果,将这个表达式提炼到一个独立的函数中,将这个临时变量所有的引用点替换为对新函数的调用。此后新函数就可被其他函数使用。
范例:重构前
double basePrice=_quantity*itemPrice;
if(basePrice>1000){
return basePrice*0.95;
}else{
return basePrice*0.98;
}
范例:重构以后
if(getBasePrice()>1000){
return getBasePrice()*0.95;
}else{
return getBasePrice()*0.98;
}
public double getBasePrice(){
return _quantity*itemPrice;
}
动机:临时变量是暂时的,只能在所属函数内使用,由于临时变量只在所属函数内可见,所以它会驱使你写出更长的函数,因为只有这样你才能访问到需要的临时变量。
范例:
double getPrice(){
int basePrice=_quantity*_itemPrice;
double discountFactory;
if(basePrice>1000){
discountFactory=0.98;
}else{
discountFactory=0.95;
}
return basePrice*discountFactory;
}
步骤一 先把临时变量申明为final类型的 方便做整体的替换 避免遗漏
double getPrice(){
final int basePrice=_quantity*_itemPrice;
final double discountFactory;
if(basePrice>1000){
discountFactory=0.98;
}else{
discountFactory=0.95;
}
return basePrice*discountFactory;
}
步骤二 使用replace temp with query 先把临时变量替换掉
double getPrice(){
final int basePrice=
final double discountFactory;
if(basePrice>1000){
discountFactory=0.98;
}else{
discountFactory=0.95;
}
return basePrice*discountFactory;
}
public int getBasePrice(){
return _quantity*_itemPrice;
}
public double getFactory(){
final double discountFactory;
if(getBasePrice()>1000)
return discountFactory=0.98;
return discountFactory=0.95;
}
步棸三 把函数中的临时变量替换为函数
double getPrice(){
return getBasePrice()*getFactory();
}
public int getBasePrice(){
return _quantity*_itemPrice;
}
public double getFactory(){
final double discountFactory;
if(getBasePrice()>1000)
return discountFactory=0.98;
return discountFactory=0.95;
}
Introduce Parameter Object(引入参数对象)
某些参数总是很自然的同时出现,以一个对象取代这些参数
范例
重构前
动机:你常会看到特定的一组参数总是一起被传递。可能有好几个函数都使用了这一组参数,这些函数可能率属于同一个类,也可能率属于不同的类,这样一组参数就是所谓的的 Data Clumps(数据泥团),我们可以用一个对象包装所有的这些数据,再以该对象取代它们。
public class A{
public void calcultorM(Date start,Date end){
System.out.println(start.getTime()-end.getTime());
}
}
public class B{
public void calculatorMC(Date start,Date end){
System.out.println(start.getTime()+end.getTime());
}
}
步骤一 先添加一个日期范围类 准备把过长或是,在一定范围内的参数用类封装起来表示
public class DateRange{
private Date start;
private Date end;
public Date getStart(){
return start;
}
public Date getEnd(){
return end;
}
public void setStart(Date start){
this.start=start;
}
public void setEnd(Date end){
this.end=end;
}
}
步骤二 用类变量取代参数
public class A{
public void calcultorM(DateRange datarange){
System.out.println(datarange.getStart().getTime()-datarange.getEnd().getTime());
}
}
public class B{
public void calculatorMC(DateRange datarange){
System.out.println(datarange.getStart().getTime()+datarange.getEnd().getTime());
}
}
public class DateRange{
private Date start;
private Date end;
public Date getStart(){
return start;
}
public Date getEnd(){
return end;
}
public void setStart(Date start){
this.start=start;
}
public void setEnd(Date end){
this.end=end;
}
}
重构完成。
Preserve Whole Object(保持对象完整)
你从某个对象中取出若干值,将他们作为某一次函数调用时的参数,改为传递整个对象
范例
int low =daysTempRange().getLow();
int high=daysTempRange().getHigh();
withinPlan=plan.withinRange(low,high);
withinPlan=plan.withinRange(daysTempRange());
动机
将来自同一个对象的若干数据作为参数,传递给某个函数,这样做的问题在于;万一将来被调用的函数需要数据项,你就必须查找和修改此函数的所有调用,如果你把这些数据作为整个对象传递,就可以便面这种尴尬的处境,因为被调用的函数可以向那个参数对象请求任何它想要的信息。
Replace Method with Method Object(以函数对象取代函数)
有一个大型的函数,对局部变量的使用无法采用Extracted Mehod,将这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段,然后你可以在同一个对象中将这个大型函数分解为多个小型函数。
动机
局部变量的存在,会增加函数分解的难度,有些时候你会发现,局部变量更本无法拆解成为一个函数,这时你应该把这些局部变量,变成对象的字段,然后再抽取成为函数。
范例
范例
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;
return importantValue3 -2 * importantValue1;
}
}
如上代码段所示,里面有大量的局部变量
我们可以使用replace method with method Object
这个方法
步骤一先构建一个含有这些局部变量的类,然后把原有的方法复制过来
class Gamma{
private final Account _account;
private int inputVal;
private int quantity;
private int yearToDate;
private int importantValue1;
private int importantValue2;
private int importantValue3;
public Gamma(Account account,int inputValArg,int quantity,int yearToDate){
this._account=account;
this.inputVal=inputValArg;
this.quantity=quantity;
this.yearToDate=yearToDate;
}
int compute(){
int importantValue1=(inputVal*quantity)+_account.delta();
int importantValue2=(inputVal * yearToDate) +100;
if((yearToDate-importantValue1)>100){
importantValue2-=20;
}
int importantValue3=importantValue2*7;
return importantValue3 -2 * importantValue1;
}
}
步骤二 用现有的方法替换原有的方法
Class Account{
int gamma(int inputVal,int quantity,int yearToDate){
return new Gamma(this,inputVal,quantity,yearToDate).compute();
}
}
场景三 大量的条件表达式与循环
重构手法
Decompose Conditional(分解条件表达式)
范例
重构前
if(date.before(SUMMER_START)||date.after(SUMMER_EEND)){
charge=quantity*_winterRate*_winterServiceCharge;
}else{
charge=quantity *_summerRate;
}
重构以后
if(notSummer(date)){
charge= winterCharge(quantity);
}else{
charge= summerCharge(quantity);
}
动机:程序中,复杂的条件逻辑是最常导致复杂度上升的地点之一。你必须编写代码来检查不同的条件分之,根据不同的分支做不同的事,然后你会得到一个相当长的函数。大型函数自身就会使代码的可读性下降,而条件逻辑则会使代码更难阅读。
完成范例
if(date.before(SUMMER_START)||date.after(SUMMER_EEND)){
charge=quantity*_winterRate*_winterServiceCharge;
}else{
charge=quantity *_summerRate;
}
重构以后
if(notSummer(date)){
charge= winterCharge(quantity);
}else{
charge= summerCharge(quantity);
}
private boolean notSummer(){
return date.before(SUMMER_START)||date.after(SUMMER_EEND);
}
private double summerCharge(){
return quantity*_summerRate;
}
private double winterCharge(){
return quantity* _winterRate*_winterServiceCharge;
}