代码重构之重新组织函数

一、提炼函数(Extract Method)

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

做法:

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

范例:

源代码

 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)

做法:

  1. 检查函数,确定它不具多态性
  2. 找出这个函数所有被调用点
  3. 将这个函数所有被调用点替换为函数本体
范例:
源代码
 private int _numberOfLateDeliveries;
    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)
做法:
  1. 检查给临时变量赋值的语句,确保等号右边的表达式没有副作用
  2. 如果这个临时变量未被声明为final,那么将他声明为final(编译检查,该变量是否只赋值一次)
  3. 找到所有该临时变量的引用点,修改为表达式,并删除这个临时变量
范例
源代码
 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)
做法:
  1. 找出只被赋值一次的临时变量
  2. 声明为final(编译检测一次性)
  3. 将这个临时变量的等号右侧提炼到独立函数中
范例:
源代码
  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)
       动机:表达式复杂而难以理解时,临时变量可以帮助容易阅读

做法:

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

范例:

源代码

  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)

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

做法:

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

源代码

 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时

做法:

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

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

做法:

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

范例:

源代码

 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;
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值