重构——改变既有代码的设计

一、重构的定义

n. 重构 : 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

v. 使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

二、重构的前提?如何理解?如何保障?重构的两顶帽子是什么?

前提:不改变软件可观测行为
理解:可观测行为是指使用软件所产生的结果。也就是说重构不能改变原代码的逻辑结果。
保障:增量式重构=自动化测试+持续集成+TDD驱动重构
两顶帽子:添加新功能和重构

二、需要重构的情况

  1. 神秘命名
  2. 重复代码
  3. 过长函数
  4. 过长参数列表
  5. 全局数据
  6. 可变数据
  7. 发散式变化
  8. 雾弹式修改
  9. 依恋情结
  10. 数据泥团
  11. 基本类型偏执
  12. 重复的switch
  13. 循环语句
  14. 冗赘的元素
  15. 夸夸其谈通用性
  16. 临时字段
  17. 过长的消息链
  18. 中间人
  19. 内幕交易
  20. 过大的类
  21. 异曲同工的类
  22. 纯数据类
  23. 被拒绝的遗赠
  24. 注释

三、如何重构

第一:第一组重构

  1. 提炼函数
  2. 内联函数
  3. 提炼变量
  4. 内联变量
  5. 改变函数声明
  6. 封装变量
  7. 变量改名
  8. 引入参数对象
  9. 函数组合成类
  10. 函数组合变换
  11. 拆分阶段

第二:封装

  1. 封装记录
  2. 封装集合
  3. 以对象取代基本类型
  4. 以查询取代临时变量
  5. 提炼类
  6. 内联类
  7. 隐藏委托关系
  8. 移除中间人
  9. 替换算法

第三:搬移特性

  1. 搬移函数
  2. 搬移字段
  3. 搬移语句到函数
  4. 搬移语句到调用者
  5. 以函数调用取代内联代码
  6. 移动语句
  7. 拆分循环
  8. 以管道取代循环
  9. 移除死代码

第四:重新组织数据

  1. 拆分变量
  2. 字段改名
  3. 以查询取代派生变量
  4. 以引用对象改为值对象
  5. 将值对象改为引用对象

第五:简化条件逻辑

  1. 分解条件表达式
  2. 合并条件表达式
  3. 以卫语句嵌套条件表达式
  4. 以多态取代条件表达式
  5. 引入特列
  6. 引入断言

第六:重构Api

  1. 将查询函数和修改函数分离
  2. 函数参数化
  3. 移除标记函数
  4. 保持对象完整性
  5. 以查询取代参数
  6. 以参数取代查询
  7. 移除设值函数
  8. 以工厂函数取代构造函数
  9. 以命令取代函数
  10. 以函数取代命令

第七:处理继承关系

  1. 函数上移
  • 将一个方法从子类移到父类中,以便多个子类可以共享该方法的实现
class ParentComponent {
  // 父类中定义共享的方法
  sharedMethod() {
    // 共享的方法实现
  }
}

class ChildComponent extends ParentComponent {
  // 子类不再需要实现 sharedMethod
  // ...
}


上述代码中,我们定义了一个父类 ParentComponent,其中包含了一个共享的方法 sharedMethod()。然后我们创建了一个子类 ChildComponent,它继承自 ParentComponent,并且不需要再实现 sharedMethod(),因为该方法已经在父类中定义了。

通过函数上移,sharedMethod() 方法从子类中移到了父类中,这样其他继承自 ParentComponent 的子类也能够共享该方法的实现。这样可以避免在每个子类中重复实现相同的方法,提高代码的可维护性和重用性。

  1. 字段下移
  • 一种处理继承关系的重构手法,用于将一个方法从父类移动到子类中,以避免让不需要使用该方法的子类也继承该方法
class ParentComponent {
  // 父类包含的一些共享的方法和属性
}

class ChildComponent extends ParentComponent {
  specificMethod() {
    // 子类特定的方法实现
  }

  // 将不再需要的方法下移到子类中
  obsoleteMethod() {
    // 不再需要的方法实现
  }
}

上述代码中,我们有一个父类 ParentComponent,其中包含了一些共享的方法和属性。然后我们创建了一个子类 ChildComponent,它继承自 ParentComponent。在子类中,我们定义了一个特定的方法 specificMethod(),它是子类独有的方法。

接着,我们将不再需要的方法 obsoleteMethod() 从父类中下移到了子类中。由于某些子类不需要使用这个方法,将其下移到子类中可以避免不必要的继承和冗余。这样,在使用 ChildComponent 的实例时,只能访问到子类中定义的方法,而无法访问到父类中下移的方法。

通过函数下移,我们可以根据具体需求将不再需要的方法下移到子类中,避免让不需要使用的子类也继承这些方法。这样可以提高代码的可读性和可维护性,减少继承层次和冗余代码。

  1. 构造函数本体上移
  • 是一种处理继承关系的重构手法,用于将子类中的构造函数本体移到父类中。这样做可以避免子类中重复的初始化逻辑,并且确保所有子类都遵循同样的初始化流程
class ParentComponent {
  constructor() {
    // 父类的构造函数本体逻辑
  }
}

class ChildComponent extends ParentComponent {
  constructor() {
    super(); // 调用父类的构造函数

    // 子类特有的构造函数本体逻辑
  }
}

上述代码中,我们定义了一个父类 ParentComponent,它有一个构造函数,在构造函数本体中包含父类特有的初始化逻辑。然后我们创建了一个子类 ChildComponent,它继承自 ParentComponent。在子类的构造函数中,我们首先通过 super() 调用父类的构造函数,以确保父类的初始化逻辑得到执行。然后在子类的构造函数本体中,可以添加子类特有的初始化逻辑。

通过构造函数本体上移,可以避免子类中重复的构造函数逻辑,并让所有子类都遵循相同的初始化流程。这样可以提高代码的重用性、可维护性和一致性。

  1. 函数下移
  • 是一种处理继承关系的重构手法,用于将一个方法从父类移动到子类中。这样做可以确保子类具备特定的行为,并避免不需要使用该方法的子类也继承了该方法

  1. 字段下移
class ParentComponent {
  sharedMethod() {
    // 父类共享的方法实现
  }
}

class ChildComponent extends ParentComponent {
  specificMethod() {
    // 子类特定的方法实现
  }
}

上述代码中,我们有一个父类 ParentComponent,其中包含了父类共享的方法 sharedMethod()。然后我们创建了一个子类 ChildComponent,它继承自 ParentComponent。在子类中,我们定义了一个特定的方法 specificMethod(),它是子类独有的方法。

然后,如果我们发现 sharedMethod() 这个方法只有在子类中会被使用,而其他子类不需要使用它,就可以进行函数下移,将该方法从父类中移到子类中:

class ParentComponent {
  // 父类包含的一些共享的方法和属性
}

class ChildComponent extends ParentComponent {
  specificMethod() {
    // 子类特定的方法实现
  }

  // 将不再需要的方法下移到子类中
  sharedMethod() {
    // 不再需要的方法实现
  }
}

通过函数下移,我们可以根据具体需求将不再需要的方法下移到子类中,避免让不需要使用的子类也继承这些方法。这样可以提高代码的可读性和可维护性,减少继承层次和冗余代码。

  1. 以子类取代类型码
  • 是一种处理对象类型区分的重构手法,通过将一个类型码作为类的属性替换为具体的子类,从而提高代码的可扩展性和可维护性。
class Shape {
  constructor(type) {
    this.type = type;
  }

  draw() {
    switch (this.type) {
      case 'circle':
        // 绘制圆形的逻辑
        break;
      case 'rectangle':
        // 绘制矩形的逻辑
        break;
      case 'triangle':
        // 绘制三角形的逻辑
        break;
      // 更多类型的逻辑...
    }
  }
}

上述代码中,我们有一个 Shape 类,它带有一个 type 属性用于表示不同的形状。然后,在 draw() 方法中,使用 switch 语句根据 type 属性执行相应的绘制逻辑。

现在,如果我们发现代码中频繁使用了 switch 语句来根据类型码进行逻辑分支,这就是一个反模式,可以使用以子类取代类型码来重构代码:

class Shape {
  draw() {
    // 抽象方法,每个子类必须实现自己的绘制逻辑
  }
}

class Circle extends Shape {
  draw() {
    // 绘制圆形的逻辑
  }
}

class Rectangle extends Shape {
  draw() {
    // 绘制矩形的逻辑
  }
}

class Triangle extends Shape {
  draw() {
    // 绘制三角形的逻辑
  }
}

// 在使用时,创建具体的子类实例,而不是通过类型码区分
const circle = new Circle();
circle.draw();

const rectangle = new Rectangle();
rectangle.draw();

const triangle = new Triangle();
triangle.draw();

上述代码中,我们将 Shape 类修改为抽象基类,并创建了具体的子类 Circle、Rectangle 和 Triangle 来表示不同的形状。每个子类都必须实现自己的绘制逻辑。这样,我们就可以根据具体的子类来调用 draw() 方法,而不再需要使用类型码进行逻辑分支判断。

通过以子类取代类型码重构,可以提高代码的可扩展性和可维护性。当需要添加新的形状时,只需要创建一个新的子类即可,不需要修改原有的代码逻辑。同时,代码也更加清晰和易懂,减少了条件分支的复杂度。

  1. 移除子类
  • 有时候我们可能会发现某些子类不再需要存在,或者某些子类的行为与父类完全一致,这时可以考虑移除这些子类
class Shape {
  draw() {
    // 绘制形状的逻辑
  }
}

class Circle extends Shape {
  draw() {
    // 绘制圆形的逻辑
  }
}

class Rectangle extends Shape {
  draw() {
    // 绘制矩形的逻辑
  }
}

class Triangle extends Shape {
  draw() {
    // 绘制三角形的逻辑
  }
}

上述代码中,我们有一个基类 Shape 表示形状,以及三个子类 Circle、Rectangle 和 Triangle 分别表示圆形、矩形和三角形。每个子类都有自己特定的 draw() 方法实现。

现在假设我们发现 Rectangle 类的绘制逻辑与基类 Shape 完全相同,可以考虑将 Rectangle 类移除,直接在基类中处理矩形的绘制逻辑。可以这样修改代码:

class Shape {
  draw() {
    // 绘制形状的逻辑
  }

  drawRectangle() {
    // 绘制矩形的逻辑
  }
}

class Circle extends Shape {
  draw() {
    // 绘制圆形的逻辑
  }
}

class Triangle extends Shape {
  draw() {
    // 绘制三角形的逻辑
  }
}

通过将 Rectangle 类移除,我们直接在基类 Shape 中添加了一个新的方法 drawRectangle(),用于绘制矩形。这样,在使用的时候,只需要根据具体的形状调用相应的方法即可。

移除子类可以简化代码结构,减少不必要的继承层次和重复代码。但需要谨慎判断是否真的可以移除子类,确保不会对代码的其他部分产生负面影响,并且在移除之前,最好通过测试来验证代码的行为是否符合预期。

  1. 提炼超类
  • 有时候我们会发现多个子类之间存在相似的行为或属性,这时可以考虑将这些相似之处提取到一个超类中,以减少重复的代码
class Shape {
  constructor(type) {
    this.type = type;
  }

  draw() {
    console.log(`Drawing ${this.type}`);
  }
}

class Circle extends Shape {
  constructor(radius) {
    super('Circle');
    this.radius = radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super('Rectangle');
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    return this.width * this.height;
  }
}

上述代码中,我们有一个基类 Shape 表示形状,以及两个子类 Circle 和 Rectangle 分别表示圆形和矩形。每个子类都有自己特定的属性和方法。

现在我们发现,无论是圆形还是矩形,它们都具有一个共同的属性 type 和一个共同的方法 draw()。可以考虑将这部分相似的代码提炼到一个超类中。可以这样修改代码:

class Shape {
  constructor(type) {
    this.type = type;
  }

  draw() {
    console.log(`Drawing ${this.type}`);
  }
}

class Circle extends Shape {
  constructor(radius) {
    super('Circle');
    this.radius = radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super('Rectangle');
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    return this.width * this.height;
  }
}

通过创建超类 Shape,我们将共同的属性 type 和方法 draw() 提取出来,然后让子类继承超类。这样,子类就不再需要重复定义这些相同的行为或属性。

提炼超类可以减少代码冗余,并且使代码更加清晰和易于维护。同时,当需要对共同行为或属性进行修改时,只需要修改超类即可,避免了逐个修改子类的麻烦。但需要注意的是,在提炼超类之前,需要仔细考虑子类之间是否真的存在共同的行为或属性,确保提炼出的超类具有一定的通用性和合理性

  1. 折叠继承体系
  • 折叠继承体系是指将多层继承关系简化为单层或更少层的继承结构
class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} is eating.`);
  }
}

class Mammal extends Animal {
  constructor(name) {
    super(name);
  }

  sleep() {
    console.log(`${this.name} is sleeping.`);
  }
}

class Dog extends Mammal {
  constructor(name) {
    super(name);
  }

  bark() {
    console.log(`${this.name} is barking.`);
  }
}

上述代码中,我们有一个基类 Animal 表示动物,以及两个子类 Mammal 和 Dog 分别表示哺乳动物和狗。每个子类都有自己特定的方法。

现在假设我们发现 Mammal 类只是简单地继承了 Animal 类,并没有添加新的属性或方法。可以考虑折叠继承体系,将 Mammal 类移除,直接在 Dog 类中集成 Animal 类的功能。可以这样修改代码:

class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} is eating.`);
  }

  sleep() {
    console.log(`${this.name} is sleeping.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  bark() {
    console.log(`${this.name} is barking.`);
  }
}

通过折叠继承体系,我们移除了中间的 Mammal 类,并将 Animal 类的功能直接集成到 Dog 类中。这样,Dog 类不再需要经过多层继承才能获得 Animal 类的功能。

折叠继承体系可以简化代码结构,减少继承的层次和复杂性。但需要注意的是,在进行折叠继承体系之前,需要确保移除的类没有额外的功能或属性,并且在折叠继承体系之后,最好通过测试来验证代码的行为是否符合预期。同时,还需要考虑代码的可读性和可维护性,确保折叠后的继承关系更加清晰和易于理解。

  1. 以委托取代子类
  • 有时候我们可能会发现子类的继承关系过于复杂,或者存在多个子类只是为了覆盖父类的部分行为。这时可以考虑使用委托模式来取代子类,通过将特定的行为委托给其他对象来达到相同的效果
class Shape {
  constructor(type) {
    this.type = type;
  }

  draw() {
    console.log(`Drawing ${this.type}`);
  }
}

class Circle {
  constructor(radius) {
    this.shape = new Shape('Circle');
    this.radius = radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }

  draw() {
    this.shape.draw();
    console.log(`Radius: ${this.radius}`);
  }
}

class Rectangle {
  constructor(width, height) {
    this.shape = new Shape('Rectangle');
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    return this.width * this.height;
  }

  draw() {
    this.shape.draw();
    console.log(`Width: ${this.width}, Height: ${this.height}`);
  }
}

上述代码中,我们有一个基类 Shape 表示形状,以及两个子类 Circle 和 Rectangle 分别表示圆形和矩形。每个子类都有自己特定的属性和方法。

现在假设我们发现 Circle 和 Rectangle 类中的 draw() 方法只是简单地调用了基类 Shape 的 draw() 方法,并且输出了一些特定的信息。可以考虑以委托取代子类,将这部分特定行为委托给另一个对象,而不是通过继承来实现。可以这样修改代码:

class Shape {
  constructor(type) {
    this.type = type;
  }

  draw() {
    console.log(`Drawing ${this.type}`);
  }
}

class DrawingService {
  constructor(shape) {
    this.shape = shape;
  }

  draw() {
    this.shape.draw();
  }
}

class Circle {
  constructor(radius) {
    this.shape = new Shape('Circle');
    this.radius = radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }

  draw() {
    const drawingService = new DrawingService(this.shape);
    drawingService.draw();
    console.log(`Radius: ${this.radius}`);
  }
}

class Rectangle {
  constructor(width, height) {
    this.shape = new Shape('Rectangle');
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    return this.width * this.height;
  }

  draw() {
    const drawingService = new DrawingService(this.shape);
    drawingService.draw();
    console.log(`Width: ${this.width}, Height: ${this.height}`);
  }
}

通过以委托取代子类,我们将特定的绘制行为封装在 DrawingService 对象中,并在子类中使用该对象来执行绘制操作。这样既避免了多层继承的复杂性,也将特定行为委托给其他对象处理。

以委托取代子类可以简化继承关系,减少子类的数量,并且使代码更加灵活和可扩展。但需要注意的是,在应用委托模式之前,需要仔细考虑具体的委托关系和实现方式,确保委托对象拥有足够的功能和合理的接口设计。同时,还需要权衡使用继承或委托的优缺点,选择最合适的方案来解决问题。

  1. 以委托取代超类
  • 有时候我们可能会发现一个超类(父类)的功能过于庞大,包含了多个不同的子功能,或者超类的职责不够清晰。这时可以考虑使用以委托取代超类的方式进行重构,将超类的部分功能委托给其他对象处理,以达到简化超类、解耦和提高可维护性的目的。
class SuperClass {
  constructor() {
    this.subFunctionA();
    this.subFunctionB();
    this.subFunctionC();
  }

  subFunctionA() {
    console.log('SuperClass: subFunctionA');
  }

  subFunctionB() {
    console.log('SuperClass: subFunctionB');
  }

  subFunctionC() {
    console.log('SuperClass: subFunctionC');
  }
}

class DelegateClass {
  subFunctionA() {
    console.log('DelegateClass: subFunctionA');
  }

  subFunctionB() {
    console.log('DelegateClass: subFunctionB');
  }
}

// 在需要使用的地方
class ClientClass {
  constructor() {
    this.delegate = new DelegateClass();
    this.delegate.subFunctionA();
    this.delegate.subFunctionB();
  }
}

在上述示例中,我们有一个超类 SuperClass,其中包含了三个子功能函数 subFunctionA、subFunctionB 和 subFunctionC。我们希望将 subFunctionA 和 subFunctionB 两个子功能委托给其他对象处理。

于是,我们创建了一个新的类 DelegateClass,其中包含了与超类中需要委托的函数对应的实现。

在需要使用的地方,比如 ClientClass 中,我们引入了委托类 DelegateClass 的实例,并在构造函数中调用委托对象的相应函数,从而实现了将部分功能委托给其他对象处理的效果。这样一来,超类 SuperClass 就变得更加简洁和职责明确,子功能的具体实现由委托对象负责。

通过以委托取代超类,我们可以将超类的功能拆分到不同的对象中,使代码结构更清晰、职责更明确,并且在需要扩展或修改功能时更加灵活。同时,这种重构方式也遵循了面向对象设计中的单一职责原则和依赖倒置原则。

需要注意的是,在应用以委托取代超类的重构时,需要仔细考虑委托关系,确保委托对象能够正确处理被委托的功能,并且具有合理的接口设计。此外,为了保证代码的可读性和可维护性,建议给委托方法添加适当的注释或文档说明。

第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等,还有翻译的侯捷和熊节先生,为我们带来这么精彩的一本书。谢谢。 免责声明:本书仅供个人学习研究之用,不得用于任何商业目的,不得以任何方式修改本作品,基于此产生的法律责任本人不承担任何连带责任。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值