重构,改善既有代码的设计(理论篇)

文章目录


前言

本文是笔者毕业后的第一篇blog,将从三个方面讨论代码重构。即:1.代码重构是什么;2.常用的重构手法;3.代码中的“坏味道”。本文的姊妹篇是程序员必学的代码重构(实战篇)
本篇blog是《重构,改善代码既有代码的设计》(密码: ab5g)一文的读书笔记,读书笔记与书一起食用效果更佳哦。欢迎点赞、收藏、评论三连~,谢谢大家。

重构是什么?

谈谈定义

重构是软件内部结构的一种调整,在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。敲重点:1. 使软件更容易被理解和修改;2. 不改变软件外部行为,对外部用户/程序员不感知。

何时重构?

满足以下原则考虑重构:1. 三次原则:“事不过三,三则重构”(反感此处代码的修改,容忍不超过三次);2. 添加功能时重构;3. 修补bug时重构;4. Review代码时重构。

常用重构手法

常用的重构手法将以总结、具体做法、代码示例三个步骤来进行说明。

提炼函数(Extract Method)

将该段代码放进一个独立函数中,并让函数名称解释该函数的用途。过长函数需要注释才能让人理解,此时应抽出独立函数,使之易复用;易读;易复写。
具体做法:

1. 无局部变量:简单复制、粘贴到被提炼函数即可;
2. 有局部变量但是被提炼代码段只读取该变量,不修改:传参给被提炼函数即可;
3. 有局部变量且再赋值:
 3.1. 该变量只在被提炼代码段使用:将临时变量声明一并移入被提炼代码段,一起提炼出去;
 3.2. 该变量在被提炼代码段之外也有使用:让提炼函数返回该变量改变后的值。有人会问:如果返回变量不止一个,怎么办?安排多函数返回多个值or挑选另一块代码来提炼,每个函数只返回一个值。使用Replace Temp with Query减少临时变量。

代码示例:

public class ExtractMethod {
    /**
     * 重构前
     */
    void printOwing(double amount) {
        printBanner();
        //print Details
        System.out.println("name" + name);
        System.out.println("amount" + amount);
    }

    /**
     * 重构后
     */
    void printOwingRefactor(double amount) {
        printBanner();
        printDetails(amount);
    }

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

以查询取代临时元素(Replace Temp with Query)

将这个表达式提炼到一个独立函数中,将这个临时变量的所有引用点替换为对新函数的调用,那么新函数可被其他函数使用。
具体做法:

1. 找出被赋值一次的临时变量(如果临时变量被赋值超过多次,使用Split Temporary Variable将其分割为多个变量);
2. 将临时变量修改成final3. 将“临时变量赋值”等号右侧部分提炼到一个独立函数中 ;
4. 最后将变量替换为方法,去掉final语句。

代码示例:

public class ReplaceTempWithQuery {
    /**
     * 重构前
     * @return
     */
    double printOwing() {
        double basePrice = quality * itemPrice;
        if (basePrice > 1000) {
            return basePrice * 0.95;
        } else {
            return basePrice * 0.98;
        }
    }
    /**
     * 重构后
     * @return
     */
    double printOwingRefactor() {
        if (basePrice() > 1000) {
            return basePrice() * 0.95;
        } else {
            return basePrice() * 0.98;
        }
    }
    double basePrice() {
        return quality * itemPrice;
    }
}

分解条件表达式(Decompose Conditional)

复杂条件逻辑容易导致复杂度上去,大型函数会使代码可读性降低。可从if,then,else三个中分别提炼出独立函数。
代码示例:

public class DecomposeConditional {
    /**
     * 重构前
     */
    void refactorBefore() {
        if (data.before(SUMMER_START) || data.after(SUMMER_END)) {
            charge = quantity * winterRate + winterServiceCharge;
        } else {
            charge = quantity * summerRate;
        }
    }
    /**
     * 重构后
     */
    void refactorAfter() {
        if (notSummer(data)) {
            charge = winterCharge(quantity);
        } else {
            charge = summerCharge(quantity);
        }
    }
    ....
}

提炼类(Extract class)

某类做了两个类应该做的事,建立一个新类,将相关的字段和函数从旧类搬移至新类。
具体做法:

1. 新建类,从新类中分离旧类责任,建立“从旧类访问新类”(尽量单向,否则依赖接口)连接关系;
2. 逐步移动字段(Move Field);
3. Move Method;精简类接口,判断是否需要公开此类。

代码示例:

/**
 * 重构前
 */
class Person {
    private String name;
    private String officeAreaCode;
    private String officeNumber;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getOfficeAreaCode() {
        return officeAreaCode;
    }
    public void setOfficeAreaCode(String officeAreaCode) {
        this.officeAreaCode = officeAreaCode;
    }
    public String getTelephoneNumber() {
        return "(" + officeAreaCode + ")" + officeNumber;
    }
    public void setOfficeNumber(String officeNumber) {
        this.officeNumber = officeNumber;
    }
}
/**
 * 重构后
 */
class PersonRefactor {
    //2.建立从Person到TelePhoneNumber的连接
    TelePhoneNumber telePhoneNumber = new TelePhoneNumber();
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    TelePhoneNumber getOfficeTelephone() {
        return telePhoneNumber;
    }
    
    public String getTelephoneNumber() {
        return telePhoneNumber.getTelephoneNumber();
    }
}
//1.将电话号相关行为分离至独立类中
class TelePhoneNumber {
    //3.Move Field移动一个字段
    private String areaCode;
    private String number;
    //4.Move Method将相关函数移动至TelePhoneNumber类中
    public String getAreaCode() {
        return areaCode;
    }

    public void setAreaCode(String areaCode) {
        this.areaCode = areaCode;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }

    public String getTelephoneNumber() {
        return "(" + areaCode + ")" + number;
    }
}

提炼接口(Extract Interface)

若干用户使用类接口中的同一子集,或者两个类的接口中有部分相同,那么将相同的子集提炼至一个独立接口中。
具体做法:

1. Extract interface只能提炼共通接口,不能提炼共通代码。新建空接口;
2. 在接口中声明待提炼类的共通操作;让相关类实现上述接口;
3. 调整客户类型声明,令其使用该接口。

补充:多态满足的三个条件:继承;重写;父类引用指向子类对象:Parent p = new Child(); 实现多态的三种方法:重写、接口、抽象类和抽象方法。
代码示例:

public class ExtractInterface {
    /**
     * 重构前
     */
    double charge(Employee emp, int days) {
        int base = emp.getRate() * days;
        if (emp.hasSpecialSkill()) {
            return base * 1.05;
        } else {
            return base;
        }
    }

    /**
     * 重构后;3.调整客户端声明,令其使用该接口
     */
    double charge(Billable emp, int days) {
        int base = emp.getRate() * days;
        if (emp.hasSpecialSkill()) {
            return base * 1.05;
        } else {
            return base;
        }
    }
}

/**
 * 1.新建空接口,在接口声明待提炼类的共通操作
 */
interface Billable {
    //员工级别
    public int getRate();
    //是否有特殊技能
    public boolean hasSpecialSkill();
}

/**
 * 2.让Employee实现上述接口
 */
class Employee implements Billable {
   //....
}

以函数取代参数(Replace Parameter with Methods)

将参数计算过程提炼至独立函数中;本体内引用该函数的地方改为调用新建的函数;替换完后,修改并测试;全部替换后,移除参数。
代码示例:

public class ReplaceParameterWithMethods {
    /**
     * 重构前
     */
    void beforeRefactor() {
        //...
        int basePrice = _quantity * _itemPrice;
        discountLevel = getDiscountLevel();
        double finalPrice = discountedPrice(basePrice, discountLevel);
    }

    /**
     * 重构后
     */
    void beforeRefactor() {
        //...
        int basePrice = _quantity * _itemPrice;
        double finalPrice = discountedPrice(basePrice);
    }

    private double discountedPrice(int basePrice) {
        discountLevel = getDiscountLevel();
    }
}

保持对象完整(Preserve Whole Object)

假设你从某对象取出若干值,将它们作为某一次调用时的参数。不如改为传递整个对象。
代码示例:

public class PreserveWholeObject {
    /**
     * 重构前
     */
    int low = daysTempRange().getLow();
    int high =  daysTempRange().getHigh();
    withinPlan = plan.withinRange(low, high);

    /**
     * 重构后
     */
    withinPlan = plan.withinRange(daysTempRange());
}

隐藏“委托关系”(Hide Delegate)

客户通过一个委托类来调用另一个对象,在服务类上建立客户所需要的所有函数,用以隐藏委托关系。
代码示例:

public class HideDelegate {
    /**
     * 重构前
     */
    //此时调用链为 manager=john.getDepartment().getManager();
    class Person {
        Department department;

        public Department getDepartment() {
            return department;
        }

        public void setDepartment(Department department) {
            this.department = department;
        }
    }
    
    class Department {
        private String chargeCode;
        private Person manager;
        
        public Department(Person manager) {
            this.manager = manager;
        }

        public Person getManager() {
            return manager;
        }
        //...
    }

    /**
     * 重构后
     */
    //2.调整用户,令它只调用服务对象提供的函数
    // manager=john.getManager();
    class PersonRefactor {
        Department department;

        public Department getDepartment() {
            return department;
        }

        public void setDepartment(Department department) {
            this.department = department;
        }
        //1.对于每一个委托关系中的函数,在服务对象端建立一个简单的委托函数
        public Person getManager() {
            return department.getManager();
        }
    }
}

内联函数(Inline Method)

一个函数的本体与名称同样清楚易懂,在函数调用点插入函数本体,然后移除该函数。
代码示例:

/**
 * 重构前
 */
int getRating() {
    return moreThanFiveLateDeliveries() ? 2 : 1;
}

private boolean moreThanFiveLateDeliveries() {
    return numberOfLateDeliveries > 5;
}
/**
 * 重构后
 */
int getRatingRefactor() {
    return (numberOfLateDeliveries > 5) ? 2 : 1;
}

引入外加函数(Introduce Foreign Method)

你需要为提供的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
代码示例:

/**
 * 代码重构前
 */
public void beforeRefactor() {
    Date newStart = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1);
}

/**
 * 代码重构后
 */
public void afterRefactor() {
    Date newStart = nextDay(previousEnd);
}

private static Date nextDay(Date previousEnd) {
    return new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1);
}

引入本地扩展(Introduce Locale Extension)

你需要为服务类额外提供一些函数,但你无法修改这个类:建立新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。
代码示例:

//1.建立扩展类,将它作为原始类的子类
class MfDataSub extends Date {
    public MfDataSub(String dataString) {
        super(dataString);
    }
    //2.在扩展类中加入转型构造函数
    public MfDataSub(Date arg) {
        super(arg.getTime());
    }

    //3.添加新特性,Move Method将所有外加函数搬移至扩展类
    Date nextDay() {
        return new Date(getYear(), getMonth(), getDate() + 1);
    }
}

以委托取代继承(Replace Inheritance with Deegation)

某子类只使用超类接口中的一部分,或是根本不需要继承而来的数据,在子类中新建一个字段用于保存超类,调整子类函数,令它改成委托超类,去掉两者间继承关系。
代码示例:

/**
 * 重构前
 */
class MyStack extends Vector {
    public void push (Object element) {
        insertElementAt(element, 0);
    }
    
    public Object pop() {
        Object result = firstElement();
        removeElementAt(0);
        return result;
    }
}
/**
 * 重构后
 */
//3.去除两个类之间的继承关系,新建受拖累对象赋给受托字段
class MyStackRefactor {
    //1.在子类中新建一字段,引用超类实例,将其初始化为this
    private Vector vector = new Vector();
    //2.修改子类中的所有函数,让其不再使用超类,转而使用上述的委托字段
    public void push (Object element) {
        vector.insertElementAt(element, 0);
    }

    public Object pop() {
        Object result = vector.firstElement();
        vector.removeElementAt(0);
        return result;
    }
    //4.针对客户端所用的每一个超类函数,为它添加一个简单的委托函数。
    public int size() {
        return vector.size();
    }
    
    public boolean isEmpty() {
        return vector.isEmpty();
    }
}

代码中的“坏味道”

本节主要讲什么是“坏味道”。嗅到代码中的“坏味道”是在培养自己的判断力,即判断一个类中有多少实例变量算是太大,一个函数有多少行代码才算太长等。本节中的小标题格式为:代码中的坏味道+解决方案。

重复代码:提炼函数;以查询取代临时元素。

假设你在一个以上的地点看到相同的程序结构,设法将它们合二为一。步骤如下:

 1. 最简单的重复代码是利用Extract method提炼重复代码;
 2. 互为兄弟的子类含相同表达式,两个类先使用Pull up method将其推入超类中,若相似而非相同,应当使用Extract method将相似/差异部分隔开。
 3. 如果两个无关的类出现重复代码,那么使用Extract class将重复代码提炼到一个独立类中,在另一个类中调用新类,且需判断放在哪里最合适。

过长函数:分解条件表达式

程序越长越难以理解。小函数易理解的关键是有好名字,他人仅通过名字便可了解函数作用。步骤如下:

1.代码需要注释说明点什么,抽出独立函数并以其用途而非实现手法命名;
2.条件表达式和循环也是提炼信号,条件表达式可使用Decompose Conditional处理表达式。
3.运用Extract Method将许多参数和临时变量当做参数传递给被提炼出来的函数;
4.运用Replace Temp With Query来消除临时元素。

过长的类:提炼类、提炼接口

过长的类会导致代码重复、混乱。Extracrt class将几个变量提炼至新类,选择彼此相关的变量;若提炼适合作为一个子类,Extract subClass往往简单。有个小技巧:使用Extract Interface为每一种使用方式提炼出一个接口,可以帮你分解此类。

过长参数列表:以函数取代参数;传值改为传对象;组装对象

太长参数会导致前后不一致、难以理解、不易使用;全局对象有很多弊端;对象可以有效地解决这一切。

1. 如果向已有对象发出一条请求可以取代一个参数,那么应该使用Replace Parameter with Methods2. 可以使用Preserve Whole Object来将同一对象一堆数据收集起来;
3. 某些数据缺乏合理归属,使用Introduce Parameter Object(引入参数对象)为他们制造一个参数对象。

发散式变化:提炼类、细粒度化

某个类因不同的原因在不同的方向上发生变化,针对外界变化的所有相应修改,都只应发生在单一类中,找出某特定原因造成的所有变化。使用Extract Class将它们提炼至另一个类(有可能是子类)中。(拆类、细粒度化)

霰弹式修改:收敛类

每遇到变化,你得在许多不同的类中做出许多小修改,不累么?步骤如下:

使用Move Method(当两类之间太多耦合,将某个类中的方法移动至另一类中)和Move Field(在目标类中新建一个字段,修改源字段的所有用户,令它们改用新字段)将需要修改的代码放在同一类中,若没有,就创造一个,将一系列行为放进同一类中。(收敛类,将一系列相关行为放入同一类中)

依恋情结:消除分散,加强封装

函数对别的类的调用超过对自己所处类的调用。Extract Method 和Move Method用起来。(数据+操作=封装)
假设某函数会用到几个类的功能。判断哪个类拥有最多被此函数使用的数据,然后就把这个函数和那些数据放在一起。保持变化只在一处发生。(消除分散+加强封装)

数据泥团:抽出新对象

很多地方都会看到相同的三四项数据:两个类中的相同字段、函数签名中的相同参数等等。解决方案如下:

1. 针对相同字段,运用Extract Class将它们提炼至独立对象;
2. 针对签名中的相同参数,运用Introduce ParameterObject(引入参数对象:以一个对象取代这些参数,因某些参数总是很自然的出现)或Preserve Whole Object(保持对象完整)。
3. 不必在意只用到新对象的一部分,只要新对象取代两个/更多的字段,就可行。

基本类型偏执:以对象取代基本类型

数据有两种:结构型(String,Data)和基本类型(int,double)。尽量以对象取代基本类型:

1.使用Replace Data Value with Object(以对象取代数据值)将本单独存在的数据值替换为对象。
2.使用Replace Array With Object(对象取代数组,对于数据组中的每个元素,以一个字段来表表示)。
3.总之:灵活使用、抽出总在一起的数据并封装对象。

switch过多:改为多态

面向对象即少用switch,switch意味着重复,使用多态解决。

1. Extract Methodswitch提炼到一个独立函数中,
2. Move Method将其搬到需要多态性的类中。此时,你可使用Replace Type Code With SubClass(以子类取代类型码:针对一个不可变的类型码,它会影响类的行为)或Replace Type Code With state/strategy(以状态对象取代类型码:针对类型码,它会影响类的行为,且无法通过继承来消除)
3. 如果只在单一函数中有些选择示例,且不想改动它们,那么Replace Parameter with Explicit Methods(以明确函数取代参数:针对参数的每一个可能值,建立独立函数。针对函数取决于参数值而选择不同行为)是个不错的选择。

平行继承体系:消除重复、消灭类似父类

当你为某类新加一个子类,那么必须要在另一个类中也要新加子类。消除重复性的步骤是:

让一个继承体系的实例引用另一个继承体系的实例,在使用Move MethodMove Field将引用端继承体系消灭掉。

冗赘类:折叠继承体系、内联化类

1. 针对子类未做足够工作的情况,使用Collapse Hierarchy(折叠继承体系:超类和子类之间并无太大区别,将它们合为一体)来做足够工作;
2. 针对几乎没用的组件,可以使用Inline class(将类内联化:某个类没做太多事情,将这个类的所有特性搬移至另一个类中,然后移除原类)来处理。

夸夸其谈未来性:警惕代码过度/提前设计

过度设计会导致系统更难理解和维护。

1. 假设某抽象类没啥用,使用Collapse Hierarchy(折叠继承体系);
2. 假设函数无必要,使用Inline class(将类内联化);
3. 假设参数未用上,使用Remove Parameter(移出参数:函数本体无需某参数,将该参数去掉);
4. 假设函数命名有点扯,使用Rename Method(函数改名:函数名称未能揭示函数的用途,修改函数名称)

令人迷惑的临时变量:提炼类、减少临时变量使用

看到一个未使用过的临时变量,会让人疯的。

假设类中有复杂算法,牵扯到好几个变量,实现者不希望传一堆参数,那么Extract Class将这些变量和相关函数提炼到一个独立类中,提炼后的新对象将是一个函数对象。

过度耦合消息链:隐藏“委托关系”、减少链式调用

一对象请求另一对象,再往后请求另一个…。消息链的产生让代码结构耦合,这时应当使用Hide Delegate(隐藏“委托关系”)。看看消息链最终得到的对象是干什么,Extract Method提炼独立函数,Move Method推入消息链。

中间人:移除中间人、内联函数

对象的封装特点是对外隐藏细节。封装伴随着委托,也要防止过度委托。

1. 某类接口超过半数函数都委托给其他类是不合适的,使用Remove Middle Man(移除中间人:某个类做了过多的简单委托动作,让客户直接调用委托类,与Hide Delegate恰好相反)2. 函数“不干实事”,可以使用Inline Method(内联函数:一个函数的本体与名称同样清晰易懂,在函数调用点插入函数本体、然后移除该函数)

狎昵关系:改双向关联为单向关联,提炼类

恋人之间花费较多时间探究彼此private的东西无可厚非,但过分狎昵的类必须拆散。

1. 使用Move MethodMove Field划清界限;
2. 尝试运用Change Bidirectional Association to Undirectional(将双向关联改为单向关联:两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。因此去除不必要的关联);
3. 如果两个类实在情投意合,那么Extract Class将共同点提炼至安全地点,
4. 或者使用HideDeletegate隐藏“委托关系”。甚至可以去掉子类与超类的继承关系。

异曲同工的类:重命名、搬移函数、提炼超类

1. 如果两个函数做同一件事却拥有不同的名称,使用Rename Method(函数重命名)2. 如果不够,可以使用Move Method(搬移函数:将某些行为移入类,直到两者协议一致)
3. 如果代码仍然有冗余,那么Extract SuperClass(提炼超类:两个类有相似特性,为两个类建立超类,将相同特性移至超类)

不完美的库类:引入外加函数、引入本地扩展

类库不够完美,在库上做一层封装。使用Introduce Foreign Method(引入外加函数)或Introduce Locale Extension(引入本地扩展)。

纯稚数据类:封装字段;移除设值函数;隐藏函数。

1. 数据类只有字段和访问字段的函数,使用Encapsulate Field(封装字段:你的类中存在一个public字段,将它声明为private,并提供相应的访问函数);
2. 如果有集合,那么使用Encapsulate Collection(封装集合:有的返回函数有集合,让这个函数返回该集合的一个只读副本;并提供添加/移除集合元素的函数);
3. 如果类中有某些字段不想被修改,那么使用Remove Setting Method(移除设置函数:类中的某个字段应该在对象创建时被设值,然后就不再改变,去掉该字段的设值函数,将该字段声明为final)4. 如果函数没啥用,可以使用Hide Method(隐藏函数:当一函数未被任何类调用或者提供过多行为的接口时,就必须将该函数声明为private

被拒绝的馈赠:字段/方法下移;以委托取代继承

1. 所有超类都应该是抽象的。子类应当继承自超类的函数和数据,
2. 但如果子类不想继承,新建子类的兄弟类,使用Push Down Method(方法下移:超类中的某函数只与部分子类相关,将这个函数移到相关子类中去)和Push Down Field(字段下移:超类中的某字段只与部分子类相关,将这个字段移到相关子类中去)。
3. 如果不想修改继承体系,那么使用Replace Inheritance with Delegation(以委托取代继承)来达到目的。

过多的注释:记录将要干什么、无十足把握代码

当你感觉需要撰写注释时,可以尝试重构,试着让注释变得多余。如果你需要记录将要干什么或者对这部分代码无十足把握(自己“为什么做某事”),这时需要写注释。

第1章 重构,第一个案例 1 1.1 起点 1 1.2 重构的第一步 7 1.3 分解并重组statement() 8 1.4 运用多态取代与价格相关的条件逻辑 34 1.5 结语 52 第2章 重构原则 53 2.1 何谓重构 53 2.2 为何重构 55 2.3 何时重构 57 2.4 怎么对经理说 60 2.5 重构的难题 62 2.6 重构设计 66 2.7 重构与性能 69 2.8 重构起源何处 71 第3章 代码的坏味道 75 3.1 Duplicated Code(重复代码) 76 3.2 Long Method(过长函数) 76 3.3 Large Class(过大的类) 78 3.4 Long Parameter List(过长参数列) 78 3.5 Divergent Change(发散式变化) 79 3.6 Shotgun Surgery(霰弹式修改) 80 3.7 Feature Envy(依恋情结) 80 3.8 Data Clumps(数据泥团) 81 3.9 Primitive Obsession(基本类型偏执) 81 3.10 Switch Statements(switch惊悚现身) 82 3.11 Parallel InheritanceHierarchies(平行继承体系) 83 3.12 Lazy Class(冗赘类) 83 3.13 Speculative Generality(夸夸其谈未来性) 83 3.14 Temporary Field(令人迷惑的暂时字段) 84 3.15 Message Chains(过度耦合的消息链) 84 3.16 Middle Man(中间人) 85 3.17 Inappropriate Intimacy(狎昵关系) 85 3.18 Alternative Classes with Different Interfaces(异曲同工的类) 85 3.19 Incomplete Library Class(不完美的库类) 86 3.20 Data Class(纯稚的数据类) 86 3.21 Refused Bequest(被拒绝的遗赠) 87 3.22 Comments(过多的注释) 87 第4章 构筑测试体系 89 4.1 自测试代码的价值 89 4.2 JUnit测试框架 91 4.3 添加更多测试 97 第5章 重构列表 103 5.1 重构的记录格式 103 5.2 寻找引用点 105 5.3 这些重构手法有多成熟 106 第6章 重新组织函数 109 6.1 Extract Method(提炼函数) 110 6.2 Inline Method(内联函数) 117 6.3 Inline Temp(内联临时变量) 119 6.4 Replace Temp with Query(以查询取代临时变量) 120 6.5 Introduce Explaining Variable(引入解释性变量) 124 6.6 Split Temporary Variable(分解临时变量) 128 6.7 Remove Assignments to Parameters(移除对参数的赋值) 131 6.8 Replace Method with Method Object(以函数对象取代函数) 135 6.9 Substitute Algorithm(替换算法) 139 第7章 在对象之间搬移特性 141 7.1 Move Method(搬移函数) 142 7.2 Move Field(搬移字段) 146 7.3 Extract Class(提炼类) 149 7.4 Inline Class(将类内联化) 154 7.5 Hide Delegate(隐藏“委托关系”) 157 7.6 Remove Middle Man(移除中间人) 160 7.7 Introduce Foreign Method(引入外加函数) 162 7.8 Introduce Local Extension(引入本地扩展) 164 第8章 重新组织数据 169 8.1 Self Encapsulate Field(自封装字段) 171 8.2 Replace Data Value with Object(以对象取代数据值) 175 8.3 Change Value to Reference(将值对象改为引用对象) 179 8.4 Change Reference to Value(将引用对象改为值对象) 183 8.5 Replace Array with Object(以对象取代数组) 186 8.6 Duplicate Observed Data(复制“被监视数据”) 189 8.7 Change Unidirectional Association to Bidirectional(将单向关联改为双向关联) 197 8.8 Change Bidirectional Association to Unidirectional(将双向关联改为单向关联) 200 8.9 Replace Magic Number with Symbolic Constant(以字面常量取代魔法数) 204 8.10 Encapsulate Field(封装字段) 206 8.11 Encapsulate Collection(封装集合) 208 8.12 Replace Record with Data Class(以数据类取代记录) 217 8.13 Replace Type Code with Class(以类取代类型码) 218 8.14 Replace Type Code with Subclasses(以子类取代类型码) 223 8.15 Replace Type Code with State/Strategy(以State/Strategy取代类型码) 227 8.16 Replace Subclass with Fields(以字段取代子类) 232 第9章 简化条件表达式 237 9.1 Decompose Conditional(分解条件表达式) 238 9.2 Consolidate Conditional Expression(合并条件表达式) 240 9.3 Consolidate Duplicate Conditional Fragments(合并重复的条件片段) 243 9.4 Remove Control Flag(移除控制标记) 245 9.5 Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式) 250 9.6 Replace Conditional with Polymorphism(以多态取代条件表达式) 255 9.7 Introduce Null Object(引入Null对象) 260 9.8 Introduce Assertion(引入断言) 267 第10章 简化函数调用 271 10.1 Rename Method(函数改名) 273 10.2 Add Parameter(添加参数) 275 10.3 Remove Parameter(移除参数) 277 10.4 Separate Query from Modifier(将查询函数和修改函数分离) 279 10.5 Parameterize Method(令函数携带参数) 283 10.6 Replace Parameter with Explicit Methods(以明确函数取代参数) 285 10.7 Preserve Whole Object(保持对象完整) 288 10.8 Replace Parameter with Methods(以函数取代参数) 292 10.9 Introduce Parameter Object(引入参数对象) 295 10.10 Remove Setting Method(移除设值函数) 300 10.11 Hide Method(隐藏函数) 303 10.12 Replace Constructor with Factory Method(以工厂函数取代构造函数) 304 10.13 Encapsulate Downcast(封装向下转型) 308 10.14 Replace Error Code with Exception(以异常取代错误码) 310 10.15 Replace Exception with Test(以测试取代异常) 315 第11章 处理概括关系 319 11.1 Pull Up Field(字段上移) 320 11.2 Pull Up Method(函数上移) 322 11.3 Pull Up Constructor Body(构造函数本体上移) 325 11.4 Push Down Method(函数下移) 328 11.5 Push Down Field(字段下移) 329 11.6 Extract Subclass(提炼子类) 330 11.7 Extract Superclass(提炼超类) 336 11.8 Extract Interface(提炼接口) 341 11.9 Collapse Hierarchy(折叠继承体系) 344 11.10 Form Tem Plate Method(塑造模板函数) 345 11.11 Replace Inheritance with Delegation(以委托取代继承) 352 11.12 Replace Delegation with Inheritance(以继承取代委托) 355 第12章 大型重构 359 12.1 Tease Apart Inheritance(梳理并分解继承体系) 362 12.2 Convert Procedural Design to Objects(将过程化设计转化为对象设计) 368 12.3 Separate Domain from Presentation(将领域和表述/显示分离) 370 12.4 Extract Hierarchy(提炼继承体系) 375 第13章 重构,复用与现实 379 13.1 现实的检验 380 13.2 为什么开发者不愿意重构他们的程序 381 13.3 再论现实的检验 394 13.4 重构的资源和参考资料 394 13.5 从重构联想到软件复用和技术传播 395 13.6 小结 397 13.7 参考文献 397 第14章 重构工具 401 14.1 使用工具进行重构 401 14.2 重构工具的技术标准 403 14.3 重构工具的实用标准 405 14.4 小结 407 第15章 总结 409
一直很喜欢重构这本书,但是由于自己记性不太好,书看过之后其中的方法总是记不住,于是想如果有电子版的重构书就好了,工作中遇到重构的问题可以随时打开查阅。在网上搜索了许久,发现重构这本书有英文chm版本的,而中文版的电子书只有扫描的PDF版本,用起来非常不方便。于是萌生想做一本重构工具书的想法,本来打算自己重新将重构书的内容再整理归类一下,后来发现原书的目录编排就很适合做工具书,包括坏味道分类,重构手法归类等,都有了一个比较系统的整理。因此,我利用空余时间制作了这样的一本中文的chm版重构,希望对大家有所帮助,也算对中国软件业做出一点小小的贡献。 本书基本上是取自”重构”中文版一书的内容,但格式上参照的是chm英文版的格式,还有一些格式小修改,比如第一章的重构前后代码对比。因为时间匆促,个人能力有限,本书难免存在一些缺漏,如果大家发现有问题,随时可以给我发邮件,我会尽快更新错误的内容。 最后再次感谢几位大师 Martin Fowler、Kent Beck等,还有翻译的侯捷和熊节先生,为我们带来这么精彩的一本书。谢谢。 免责声明:本书仅供个人学习研究之用,不得用于任何商业目的,不得以任何方式修改本作品,基于此产生的法律责任本人不承担任何连带责任。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张云瀚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值