重构函数

1. 提炼函数

这种情况应该很多人都遇到过,编程中不要有大量的重复代码,解决办法就是去提炼到一个单独的函数中。

void A() {
    .....
    System.out.println("name" + _name);
}

void B() {
    .....
    System.out.println("name" + _name);
}

更改为↓

void A() { .... }

void B() { .... }

void printName(String name) {
    System.out.println("name" + name);
}
void printOwing(double amount){
	String _name;
	//print details
	System.out.println("name:" +_name);
	System.out.println("amount:"+amount);
}



更改为↓
void printDetails(double amount, String _name) {
	System.out.println("name:" +_name);
	System.out.println("amount:"+amount);
}
2. 内联函数

在函数调用点插入函数本体,然后移除该函数

int getRating(){
return (moreThanFiveLateDeliveries())?2:1;
}
boolean moreThanFiveLateDeliveries(){
return _numberOfLateDeliveries>5;
}


做法:
检查函数,确定它不具多态性,如果子类继承了这个函数,就不要将此函数内联,因为子类根本无法覆写一个根本不存在的函数。
找出这个函数的所有被调用点。
将这个函数的所有被调用点都替换为函数本体
编译,测试
删除该函数的定义
3. 内联临时变量
如果你对一个变量只引用了一次,那就不妨对他进行一次重构。
将所有对该变量的引用动作,替换为对它赋值的那个表达式本身
int basePrice = order.basePrice();
return (basePrice > 100);


更改为↓

return (order.basePrice() > 1000);


做法:


 
 


 
 


 
 


 
 


 
 


 
 


 
 


 
 


 
 


 
 


 
 


 
 


 
 
检查给临时变量赋值的语句,确保等号右边的表达式没有副作用。
如果这个临时变量并未被申明为final,那么将它申明为final,然后编译,这可以检查临时变量是否只会赋值一遍。
找到该临时变量的所有引用点,将他们替换为“为临时变量赋值的表达式”。
修改完所有的引用点以后,删除临时变量的申明和赋值语句。
编译,测试。
4.以查询取代临时变量

临时变量多了会难以维护,所以尽量去掉所使用的临时变量。

程序以一个临时变量保存某一表达式的运算结果,将这个表达式提取到一个独立函数中,将这个临时变量的所有引用点替换为对新函数的调用,新函数就可以被其他函数调用。

int area = _length * _width;
if (area > 1000) 
    return area * 5;
else
    return area *4;



更改为↓

if (area() > 1000) 
    return area() * 5;
else
    return area() *4;

int area() {
    return _length * _width;
}


临时变量是暂时的,只能在所属函数中使用,所以需要写更长的函数才能访问到临时变量。把临时变量替换为一个查询,同一个类中所有函数都可以获得这份信息。
做法:
简单:
找出只被赋值一次的临时变量。如果这个临时变量被赋值超过一次,将它分割成多个变量。
将该临时变量声明为final。
编译,可确保该临时变量只被赋值一次。
将对该临时变量赋值的语句的等号的右边提炼到一个独立函数中
首先将函数声明成private,以后会有更多类需要使用它,那时在放开保护
确保提炼出来的函数无任何副作用,及该函数并不修改任何对象内容,有则提炼
编译,测试。
在该临时变量身上实施内联临时变量
double getPrice(){
	int basePrice = _quantity*_itemPrice;
	double discountFactor;
	if(basePrice >1000) discountFactor = 0.95;
	else discountFactor = 0.98;
	return basePrice*discountFactor;
}


 double getPrice(){
	//检查是否赋值一次
	//final double discountFactor = discountFactor();
	//if(basePrice() >1000) discountFactor = 0.95;
	// else discountFactor = 0.98;
	return basePrice()*discountFactor();
 }
	  
private int basePrice() {
	return  _quantity*_itemPrice;//使用内联临时变量把basePrice的引用点去掉。然后去掉临时变量的声明
}
private double discountFactor(){
	if(basePrice()>1000) return 0.95;
	else return 0.98;
}






















































5. 引入解释性变量

跟上面那个相反,如果使用函数变得很复杂,可以考虑使用解释型变量了。

将该复杂表达式(或其中一部分)的结果放入一个临时变量,以此变量名称来解释表达式用途

if ((platform.toUpperCase().indexOf("mac") > -1) &&
    (brower.toUpperCase().indexOf("ie") > -1) &&
    wasInitializes() && resize > 0) {
        ......
    }


更改为↓

final boolean isMacOS = platform.toUpperCase().indexOf("mac") > -1;
final boolean isIEBrowser = brower.toUpperCase().indexOf("ie") > -1;
final boolean wasResized = resize > 0;

if (isMacOS && isIEBrowser && wasInitializes() && wasResized) {
    ......
}


做法:
声明一个final临时变量,将待分解的复杂表达式中的一部分动作的运算结果赋值给它。
将表达式中的运算结果这一部分,替换为上述临时变量。如果被替换的这一部分在代码中重复出现,可以卓一替换。
编译,测试。
处理表达式其余部分。

6.   分解临时变量
某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。
针对每次赋值,创造一个独立,对应的临时变量。
 	
double temp = 2*(_height+_width);
	System.out.println(temp);
	temp = _height*_width;
	System.out.println(temp);
	更改为↓
	final double perimeter = 2*(_height+_width);
	System.out.println(perimeter);
	final double area = _height*_width;
	System.out.println(area);



如果临时变量承担多个责任,应该被分解成多个临时变量,每个临时变量承担一个责任。
















做法:
在待分解临时变量的声明及其第一次被赋值处,修改其名称。如果赋值语句是[i=i+某表达式]形式,意味着这是个结果收集变量,就不要分解。结果收集变量的作用通常是累加、字符串接合,写入流或者向集合添加元素。
将新的临时变量声明为final。
以该临时变量的第二次赋值动作为界,修改此前对该临时变量的所有引用点,让他们引用新的临时变量。
在第二次赋值处,重新声明那个临时变量。
编译,测试。
重复操作,每次都在声明处对临时变量改名,并修改下次赋值之前的引用点

7. 移除对参数的赋值

参数传入函数中,应该尽量避免对其进行更改。

代码对一个参数进行赋值,以一个临时变量取代该参数的位置。

int discount (int inputVal, int quantity, int yearToDate) {
    if (inputVal > 50) inputVal -= 2;
}
更改为↓
int discount (int inputVal, int quantity, int yearToDate) {
    int result = inputVal;
    if (result > 50) result -= 2;
}


另外,函数中声明的临时变量最好只被赋值一次,如果超过一次就考虑再声明变量对其进行分解了。
一个函数也不应该太长,如果太长首先影响理解,其次包含的步骤太多会影响函数复用。做法是将里面的步骤提取为很多小函数,并且函数命名要体现出函数做了什么,清晰明了。

做法:

建立一个临时变量,把待处理的参数赋予它。

以对参数的赋值为界,将其后所有对此参数的引用点,全部替换为‘对此临时变量的引用’。

修改赋值语句,使其改为对新建的临时变量赋值。

编译,测试。如果代码的语义是按引用传递的,在调用端检查调用后是否还是用这个参数,也要检查有多少个按引用传递的参数被赋值后又被使用,尽量只以return方式返回一个值,如果返回的值不止一个,把返回的值改成对象,或为每个返回值设计对应的独立函数。

 

int distinct(int inputVal,int quantity,int yearToDate){
 if(inputVal >50) inputVal =2;
 if(quantity >100) inputVal =1;
 if(yearToDate >10000) inputVal =4;
 return inputVal;
 }
更改为↓
 //以临时变量取代对参数的赋值动作
 //为参数加上final,强调遵循不对参数赋值这一惯例
 int distinct(final int inputVal,final int quantity,final int yearToDate){
 int result = inputVal;
 if(inputVal >50) result =2;
 if(quantity >100) result =1;
 if(yearToDate >10000) result =4;
 return result;
 }

8.以函数对象取代函数

大型函数,对局部变量的使用让无法使用提炼函数

将这个函数放入单独对象中,这样局部变量就变成了对象中的字段,可以在同一个对象中将大型函数分解成多个小型函数。

做法:

建立一个新类,根据待处理函数的用途,以这个类命名。

在新类中建立一个final字段,用来保存原先大型函数所在的对象,将这个字段称为源对象,同时,原函数的每个临时变量和每个参数,在新类中建立一个对应的字段保存。

在新类中建立一个构造函数,接收源对象及原函数的所有参数作为参数。

在新类中建立一个compute()函数。

将原函数的代码复制到compute()中,如果需要调用源对象的任何函数,通过源对象字段调用。

编译。

将旧函数的函数本体替换为这样一条语句:‘创建上述新类的一个新对象,而后调用其中的compute()函数’。

范例:

class Account{
 int gamma (int inputVal ,int quantity,int yearToDate){
//  int importantValue1 = (inputVal*quantity)+ delte();
//  int importantValue2 = (inputVal*quantity)+100;
//  if((yearToDate - importantValue1)>100)
//  importantValue2 -=20;
//  int importantValue3 = importantValue2 * 7;
//  return importantValue3 - 2 * importantValue1; 
//  }
 return new Gamma(this, inputVal, quantity, yearToDate).compute();
 }
 }


class Gamma{
//提供final保存源对象
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 source, int inputVal, int quantity, int yearToDate) {
super();
this._account = source;
this.inputVal = inputVal;
this.quantity = quantity;
this.yearToDate = yearToDate;
}
//把旧函数的放入
int compute(){
int importantValue1 = (inputVal*quantity)+ _account.delte();
 int importantValue2 = (inputVal*quantity)+100;
 //抽函数
 importantThing();
 int importantValue3 = importantValue2 * 7;
 return importantValue3 - 2 * importantValue1;
}
void importantThing() {
if((yearToDate - importantValue1)>100)
 importantValue2 -=20;
}

}


9.替换算法

将函数本体替换成另一个算法。

String foundPersion(String [] people){
 for(int i = 0; i<people.length;i++){
 if (people[i].equals("Don")) {
return "Don";
 }
 if(people[i].equals("Join")){
 return "Join";
 }
 if(people[i].equals("Kent")){
 return "Kent";
 }
 }
 return "";
 }
 
更改为↓
 String foundPersion(String [] people){
 List candidates = Arrays.asList(new String []{"Don","Join","Kent"});
 for(int i = 0; i<candidates.size();i++){
 if(candidates.contains(people[i])){
 return people[i];
 }
 }
 return "";
 }



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值