《重构-改善既有代码的设计》学习笔记

Posted 躲猫猫的猫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《重构-改善既有代码的设计》学习笔记相关的知识,希望对你有一定的参考价值。

 昨天讲了那么多坏味道,今天就说下该怎样清理那些坏味道。

1.提炼函数(Extract Method)

将一段代码放进一个独立函数中,并让函数名称解释该函数的用途。

void printOwing(double amount){
        printBanner();

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

↓↓
↓↓↓↓↓↓替换
void printOwing(double amount){
        printBanner();
        printDetails(amount);
    }

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

动机:简短而命名良好的函数有以下几个优点: 
  - 如果每个函数的粒度都很小,那么函数被复用的机会就更大 
  - 会使高层函数读起来就像一系列注释 
  - 如果函数都是细粒度,那么函数的覆写也会更容易些

注意:创建函数时,根据这个函数的意图来对它命名(以它“做什么”来命名,而不是以它“怎样做”命名)

 

2.内联函数(Inline Method)

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

int getRating(){
        return (moreThanFiveLateDeliveries()) ? 2 : 1;
    }

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

动机:当你遇到某些函数,其内部代码和函数名称同样清晰易读时,就可以试试“内联函数”。

  如果使用了太多间接层,使得系统中的所有函数都似乎只是对另一个函数的简单委托,那就应该去除无用的间接层

 

3.内联临时变量(Inline Temp)

将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。

double basePrice = anOrder.basePrice();
    return (basePrice > 100)

↓↓↓↓↓↓↓↓替换

return (anOrder.basePrice() > 100)

动机:Inline Temp多半是作为Replace Temp with Query的一部分使用,唯一单独使用的情况是:发现某个临时变量被赋予某个函数调用的返回值。

 

4.以查询取代临时变量(Replace Temp With Query)

将这个表达式提炼到一个独立函数中,将这个临时变量的所有引用点替换为对新函数的调用,此后,新函数就可被其他函数使用

double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000)
        return basePrice * 0.95;
    else
        return basePrice * 0.98;

↓↓↓↓↓↓↓↓替换

if (basePrice() > 1000)
        return basePrice() * 0.95;
    else
        return basePrice() * 0.98;
    ...
    double basePrice(){
        return _quantity * _itemPrice;
    }

动机:临时变量的问题在于:它们是暂时的,而且只能在所属函数内使用,如果把临时变量替换为一个查询,那么同一个类中的所有函数都将可以获得这份信息

Replace Temp with Query往往是运用Extract Method之前必不可少的一个步骤,局部变量会使代码难以被提炼,所以应该尽可能把它们替换为查询式

 

5.引入解释性变量(Introduce Explaning Variable)

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

if ((platform.toUpperCase().indexOf("MAC") > -1) && (brower.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0)
    {
        // do something
    }

↓↓↓↓↓↓↓↓替换

final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
    final boolean isIEBrowser = brower.toUpperCase().indexOf("IE") > -1;
    final boolean wasResized = resize > 0;

    if (isMacOs && isIEBrowser && wasInitialized() && wasResized){
        // do something
    }

动机:表达式有可能非常复杂而难以阅读,这种情况下,临时变量可以帮助将表达式分解为比较容易管理的形式。使用这项重构的另一种情况是,在较长算法中,可以运用临时变量来解释每一步运算的意义

 

6.分解临时变量(Split Temporary Variable)

针对每次赋值,创造一个独立、对应的临时变量

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(temp);
    final double area = _height * _width;
    System.out.println(area);

动机:很多临时变量用于保存一段冗长代码的运算结果,以便稍后使用,这种临时变量应该只被赋值一次,如果它们被赋值超过一次,就意味着它们在函数中承担了一个以上的责任

  ,如果临时变量承担多个责任,就应该被替换(分解)为多个临时变量,每个变量只承担一个责任

 

7.移除对参数的赋值(Remove Assignments to Parameters)

以一个临时变量取代该参数的位置

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 (inputVal > 50) result -= 2;

动机:如果把一个名为foo的对象作为参数传给某个函数,那么”对参数赋值”意味改变foo,使它引用另一个对象,这会降低代码的清晰度,而且混用了按值传递和按引用传递两种参数传递方式。如果在”被传入对象”身上进行操作就没有问题

 

8.以函数对象取代函数(Replace Method with Method Object)

如果有一个大型函数,可以将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段,然后就可以在同一个对象中将这个大型函数分解为多个小型函数

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;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }

↓↓↓↓↓↓↓↓替换

class Gamma...
            private final Account _account;
            private int inputVal;
            private int quantity;
            private int yearToDate;
            private int importantValue1;
            private int importantValue2;
            private int importantValue3;

            Gamma(Account source, int inputValArg, int quantityArg, int yearToDateArg){
                _account = source;
                inputVal = inputValArg;
                quantity = quantityArg;
                yearToDate = yearToDateArg;
            }

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

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

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

动机:局部变量的存在会增加函数分解难度。如果一个函数之中局部变量泛滥成灾,那么想分解这个函数就非常困难。

 

9.替换算法(Substitute Algorithm)

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

String foundPerson(String[] people){
        for(int i = 0;i < people.length;i++){
            if(people[i].equals("Don")){
                return "Don";
            }
            if(people[i].equals("John")){
                return "John";
            }
            if(people[i].equals("Kent")){
                return "Kent";
            }
        }
        return "";
    }

↓↓↓↓↓↓↓↓替换

String foundPerson(String[] people){
        List candidates = Arrays.asList(new String[]{"Don", "John", "Kent"});
        for(int i = 0; i < people.length; i++){
            if(candidates.contants(people[i])){
                return people[i];
            }
            return "";
        }
    }

动机:如果发现做一件事可以有更清晰的方式,就应该以较清晰的方式取代复杂的方式

 

10.搬移函数(Move Method)

 

以上是关于《重构-改善既有代码的设计》学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

读书笔记读《重构 改善既有代码的设计》有感

《重构-改善既有代码的设计》笔记

《重构-改善既有代码的设计》读书笔记

《重构,改善既有代码的设计》读书笔记

重构·改善既有代码的设计.01

重构改善代码的既有设计