Extract Method

1 简介

在《重构 改善既有代码的设计》中,重新组织函数一章中,非常精彩的小节,也是该部分的核心重构手法为Extract Method。本文主要是对Extract Method进行整理,温故而知新。

注意:该重构手法中局部变量的含义要十分明确,局部变量包括传进源函数的参数和源函数所声明的临时变量。

2 记录格式

Extract Method_第1张图片
3 做法
下图简单阐述该重构手法的操作步骤。
Extract Method_第2张图片

4 难点

提炼函数的难点在于被提炼代码段中局部变量的处理。在原书P111页有这样的陈述。

检查被提炼代码段,看看是否有局部变量的值被它改变。如果一个临时变量值被修改了,看看是否可以将被提炼代码段处理为一个查询,并将结果赋值给相关变量。如果很难这样做,或如果被修改的变量不止一个,你就不能将这段代码原封不动的提炼出来。先使用Split Temporary Variable、Replace temp with Query优化后再进行。

使用XMind总结局部变量的处理的过程如下:
Extract Method_第3张图片

5 范例介绍

5.1 起点

    void printOwing(){
        Enmeration 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 = (Oder) e.nextElement();
            outstanding += each.getAmount();
        }
        // print details
        System.out.println("name: " + _name);
        System.out.println("amount" + outstanding);
    }

5.2 无局部变量

提炼打印横幅的代码,只需要剪切、粘贴、再插入一个函数调用即可

    void printOwing(){
        Enmeration e = _orders.elements();
        double outstanding = 0.0;
        // print banner
        printBanner();
        // calculate outstanding
        while (e.hasMoreElements()){
            Order each = (Oder) e.nextElement();
            outstanding += each.getAmount();
        }
        // print details
        System.out.println("name: " + _name);
        System.out.println("amount" + outstanding);
    }
    void printBanner(){
        System.out.println("*************************");
        System.out.println("***** Customer Owes *****");
        System.out.println("*************************");
    }

5.3 有局部变量

局部变量,包括传进源函数的参数和源函数所声明的临时变量。局部变量的作用域仅限于源函数,所以必须花费额外功夫去处理这些变量。
最简单的情况:被提炼代码只是读取这些变量的值,并不修改它们
例如上述代码片段中

    // print details
    System.out.println("name: " + _name);
    System.out.println("amount" + outstanding);

中的局部变量outstanding,近读取,而未修改,则可以简单地把它们当作参数传给目标函数。

    void printOwing(){
        Enmeration e = _orders.elements();
        double outstanding = 0.0;
        // print banner
        printBanner();
        // calculate outstanding
        while (e.hasMoreElements()){
            Order each = (Oder) e.nextElement();
            outstanding += each.getAmount();
        }
        // print details
        printDetails(outstanding);
    }
    
    //提炼后的代码
    void printDetails(double outstanding){
        System.out.println("name: " + _name);
        System.out.println("amount" + outstanding);
    }

5.4 对局部变量再赋值

Extract Method_第4张图片
提炼“计算代码”

    void printOwing(){
        // print banner
        printBanner();
        // calculate outstanding
        double outstanding = getOutStanding();
        // print details
        printDetails(outstanding);
    }

double getOutStanding(){
    Enmeration e = _orders.elements();
    double outstanding = 0.0;
    while (e.hasMoreElements()){
        Order each = (Oder) e.nextElement();
        outstanding += each.getAmount();
    }
    return outstanding;
}

可以看到outstanding随后仍要使用,因此需要返回计算后的值。这个变量(outstanding)只在被提炼代码段中使用,果真如此,可以将这个临时变量的声明移到被提炼代码段中,然后一起提炼过去。可以看到上述的优化也是这么做的把double outstanding = 0.0也一起提炼到了函数getOutStanding()中。

5.5 重命名变量名

随后将回传值改名为result

    double getOutStanding(){
        Enmeration e = _orders.elements();
        double result = 0.0;
        while (e.hasMoreElements()){
            Order each = (Oder) e.nextElement();
            result += each.getAmount();
        }
        return result;
    }

5.6 初值处理

本例中outstanding变量只是很单纯的被初始化为一个明确初值,所以我可以在心函数中对它进行初始化。如果代码还对这个变量做了其他处理,就必须将它的值作为参数传给目标函数。

5.6.1 起点

    void printOwing(double previousAmount){
        Enmeration e = _orders.elements();
        double outstanding = previousAmount * 1.2;
        // print banner
        printBanner();
        // calculate outstanding
        while (e.hasMoreElements()){
            Order each = (Oder) e.nextElement();
            outstanding += each.getAmount();
        }
        // print details
        printDetails(outstanding);
    }

5.6.2 优化

可以看到对参数outstanding进行了初值的处理,因此需要作为参数传给目标函数

    void printOwing(double previousAmount){
        double outstanding = previousAmount * 1.2;
        // print banner
        printBanner();
        // calculate outstanding
        outstanding = getOutStanding(outstanding);
        // print details
        printDetails(outstanding);
    }
    
    double getOutStanding(double initialVale){
        double result = initialVale;
        Enmeration e = _orders.elements();
        while (e.hasMoreElements()){
            Order each = (Oder) e.nextElement();
            result += each.getAmount();
        }
        return result;
    }

5.6.3 结果

    void printOwing(double previousAmount){
        // print banner
        printBanner();
        // calculate outstanding
        double outstanding = getOutStanding(previousAmount * 1.2);
        // print details
        printDetails(outstanding);
    }

6 总结

拥有短函数的对象会活得比较好,比较长。而且程序越长,越难理解。最终的效果是:你应该更积极的分解函数。我们遵循这样一条原则:每当感觉需要注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)来命名。因此百分之九十九的长河里,要把函数变小,只需要使用Extract Method。找到函数中适合集中在一起的部分,将它们提拉到一个新函数。通过这样不断的实践,可以增强代码的可读性,使得程序阅读取来非常简单,也增强了程序的可复用性。

你可能感兴趣的:(读后感)