您的位置:首页 > 编程语言

代码重构之重新组织函数

2017-11-25 15:41 417 查看
一、提炼函数(Extract Method)

       动机:当函数过长或者需要注释才能理解用途的代码

做法:

创造一个新函数,根据这个函数的意图来命名
将提炼出来的代码从源函数复制到新的目标函数中
仔细检查提炼出来的代码,看看是否引用了" 作用域限于源函数"的变量
检查是否有"仅用于被提炼函数的代码段"的临时变量,如果有在目标函数中声明临时变量
检查被提炼代码段,看看是否有局部变量的值被它改变,如果临时变量的之被改变了,看看是否可以将被提炼代码处理为一个查询,并将结果赋值给相关变量,如果很难这么做,你需要先使用Split Temporary Variable,然后尝试提炼。(也可以使用Replace Temp with Query)
将被提炼代码中需要读取的局部变量,当作参数传给目标

范例:

源代码

private Vector _orders;
private String _name;
void printOwing(){
Enumeration e = _orders.elements();
double outstanding = 0.0;
//print banner
System.out.println("****************************");
System.out.println("*******Customer Owes********");
System.out.println("****************************");

//calculate outstanding
while (e.hasMoreElements()){
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
//print details
System.out.println("name:"+_name);
System.out.println("amount:"+outstanding);
}


重构

private Vector _orders;
private String _name;
void printOwing(){
/*
无局部变量
*/
//print banner
printBanner();
/*
有局部变量,并修改
*/
//calculate outstanding
double outstanding = getOutstanding();
/*
有局部变量:只读
*/
//print details
printDetails(outstanding);
}

private double getOutstanding() {
Enumeration e = _orders.elements();
//该变量只是简单初始化,如果有其他处理,则需作为参数传进来
double outstanding = 0.0;
while (e.hasMoreElements()){
Order each = (Order)e.nextElement();
outstanding += each.getAmount();
}
return outstanding;
}

private void printDetails(double outstanding) {
System.out.println("name:"+_name);
System.out.println("amount:"+outstanding);
}

private void printBanner() {
System.out.println("****************************");
System.out.println("*******Customer Owes********");
System.out.println("****************************");
}


二、内联函数(Inline Method)

       动机:内部代码和函数名一样清晰易懂或者有一群不甚合理的函数(Replace Method with Method Object)

做法:

检查函数,确定它不具多态性
找出这个函数所有被调用点
将这个函数所有被调用点替换为函数本体
范例:
源代码
private int _numberOfLateDeliveries;
4000
int getRating(){
return (moreThanFiveLateDeliveries()) ? 2:1;
}
boolean moreThanFiveLateDeliveries(){
return _numberOfLateDeliveries>5;
}

重构
private int _numberOfLateDeliveries;
int getRating(){
return (_numberOfLateDeliveries > 5) ? 2:1;
}

三、内联临时变量(Inline Temp)
       动机:临时变量妨碍其他的重构手法时(如Replace Temp with Query)
做法:

检查给临时变量赋值的语句,确保等号右边的表达式没有副作用
如果这个临时变量未被声明为final,那么将他声明为final(编译检查,该变量是否只赋值一次)
找到所有该临时变量的引用点,修改为表达式,并删除这个临时变量

范例
源代码
public boolean test(){
Order anOrder = new Order();
double basePrice = anOrder.basePrice();
return basePrice>1000;
}


重构
public boolean test(){
Order anOrder = new Order();
return anOrder.basePrice() >1000;
}

四、以查询取代临时变量(Replace Temp with Query)
       动机:其他重构手法难以进行时(如Extract Method)
做法:

找出只被赋值一次的临时变量
声明为final(编译检测一次性)
将这个临时变量的等号右侧提炼到独立函数中
范例:

源代码
private int  _quantuty;
private int _itemPrice;
double getPrice(){
int basePrice = _quantuty*_itemPrice;
double discountFactor;
if(basePrice>1000)
discountFactor = 0.95;
else
discountFactor = 0.98;
return basePrice*discountFactor;
}

重构
private int  _quantuty;
private int _itemPrice;
double getPrice(){
double discountFactor;
return basePrice() *discountFactor();
}

private double discountFactor() {
if(basePrice() >1000)
return  0.95;
else
return  0.98;
}

private int basePrice() {
return _quantuty*_itemPrice;
}


五、引入解释性变量(Introduce Explaining Variable)
       动机:表达式复杂而难以理解时,临时变量可以帮助容易阅读
做法:

声明一个final临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给他
将表达式中的"运算结果",替换为上述临时变量

范例:

源代码

private int _quantity;
private int _itemPrice;
double price(){
//price is base price-quantity discount +shipping
return _quantity * _itemPrice-Math.max(0,_quantity-500)*_itemPrice*0.05+Math.min(_quantity*_itemPrice*0.1,100.0);
}


重构

private int _quantity;
private int _itemPrice;
double price(){
//price is base price-quantity discount +shipping
final double basePrice =  _quantity * _itemPrice;
final double quantityDiscount = Math.max(0,_quantity-500)*_itemPrice*0.05;
final double shipping = Math.min(basePrice*0.1,100.0);
return basePrice-quantityDiscount+shipping;
}

六、分解临时变量(Split Temporary Variable)

       动机:临时变量被赋值超过一次(除循环和结果收集外)

做法:

在待分解临时变量的声明及其第一次被赋值处,修改其名称
将新的临时变量声明为final
以该临时变量的第二次赋值动作为临界点,修改此前对该临时变量的所有引用点,让他们引用新的临时变量
在第二次赋值处,重新声明原先那个临时变量
重复上述过程,直到临时变量被赋值一次为止
范例:

源代码

private double _primaryForce;
private double _secondaryForce;
private double _mass;
private int _delay;
double getDistanceTravelled(int time){
double result;
double acc = _primaryForce/_mass;
int primaryTime = Math.min(time,_delay);
result = 0.5*acc*primaryTime*primaryTime;
int secondaryTime = time - _delay;
if(secondaryTime>0){
double primaryVel = acc*_delay;
acc = (_primaryForce+_secondaryForce)/_mass;
result += primaryVel=secondaryTime+0.5*acc*secondaryTime*secondaryTime;
}
return result;
}

重构

private double _primaryForce;
private double _secondaryForce;
private double _mass;
private int _delay;
double getDistanceTravelled(int time){
double result;
final double primaryAcc =  _primaryForce/_mass;
int primaryTime = Math.min(time,_delay);
result = 0.5*primaryAcc*primaryTime*primaryTime;
int secondaryTime = time - _delay;
if(secondaryTime>0){
double primaryVel = primaryAcc*_delay;
double secondaryAcc = (_primaryForce+_secondaryForce)/_mass;
result += primaryVel=secondaryTime+0.5*secondaryAcc*secondaryTime*secondaryTime;
}
return result;
}

七、以函数对象取代函数(Replace Method With Method Object)

       动机:函数中局部变量泛滥,而无法进行Extract Method时

做法:

建立一个新类,根据待处理函数用途,来命名
在新建类中建立final字段,用以保存原先大型函数所在的对象,每个临时变量和参数建立对应的字段保存。
在新类中建立构造函数,接收源对象以及原函数的所有参数作为参数
在新类中建立一个Compute()
将原函数的代码复制到compute()中,如果需要调用源对象的任何函数,通过源对象字段调用
将旧函数的函数体替换为:创建上述新类对象,并调用compute()

范例:

源代码

int gamma(int inputeVal,int quantity,int yearToDate){
int importantValue1 = (inputeVal * quantity)+delta();
int importantValue2 = (importantValue1=yearToDate)+100;
if((yearToDate-importantValue1)>100){
importantValue2 -= 20;
}
int importantValue3 = importantValue2*7;
return importantValue3 -2*importantValue1;
}

public int delta(){
return 0;
}


重构

public class ReplaceMethodWithMethodObject {

int gamma(int inputeVal,int quantity,int yearToDate){
return new Gamma(this,inputeVal,quantity,yearToDate).compute();
}

public int delta(){
return 0;
}
}

class Gamma{
private ReplaceMethodWithMethodObject object;
private int inputVal;
private int quantity;
private  int yearToDate;
private int importantValue1;
private int importantValue2;
private int importantValue3;

public Gamma(ReplaceMethodWithMethodObject object, int inputVal, int quantity, int yearToDate) {
this.object = object;
this.inputVal = inputVal;
this.quantity = quantity;
this.yearToDate = yearToDate;
}

int compute(){
importantValue1 = (inputVal * quantity)+object.delta();
importantValue2 = (importantValue1=yearToDate)+100;
importantThing();
importantValue3 = importantValue2*7;
return importantValue3 -2*importantValue1;
}

private void importantThing() {
if((yearToDate-importantValue1)>100){
importantValue2 -= 20;
}
}
}

八、移除对参数的赋值(Remove Assignment To Parameters)


       动机:被传入对象指向一个新的对象

做法:

建立临时变量,把带待处理的对象赋值给他
以"对参数的赋值"为界,将其后所有对此参数的引用,全部替换为"对此临时变量的引用"
修改赋值语句,使其改为对新建之临时变量赋值

范例:

源代码

int discount(int inputVal,int quantity,int yearToDate){
if(inputVal>50)
inputVal -=2;
if(quantity>100)
inputVal -=1;
if(yearToDate>1000)
inputVal -=4;
return inputVal;
}


重构

int discount(final int inputVal,final int quantity,final int yearToDate){
int result = inputVal;
if(inputVal>50)
result -=2;
if(quantity>100)
result -=1;
if(yearToDate>1000)
result -=4;
return result;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  函数 重构 提炼
相关文章推荐