《重构-改善既有代码的设计》系列读书笔记(二、函数的重构)

重新组织函数

过长的函数会带来一系列问题,所以想办法重构吧。

Extract Method(提炼函数)

提炼动机
  • 如果函数内部有一段代码需要使用注释来解释,请把这段代码提炼为一个新的函数
  • 函数粒度越小,被复用的机会就越大
  • 如果一个函数内部调用的都是小颗粒函数,该函数阅读起来会更方便
  • 小颗粒函数的重写会更容易
  • 小颗粒函数需要精准的命名
  • 函数名的长度不是问题,和函数本体之间的语义距离才是,应该做到见字如面
具体做法
  • 创建一个新函数,以它做什么而不是怎么做来命名,如果你想不出一个合适的名称时,先不要提炼
  • 拷贝代码到新函数
  • 检查新函数是否引入作用域限于源函数的变量
  • 检查临时变量(是否引入,是否修改)
  • 替换新函数,测试新函数

Inline Method(内联函数)

提炼动机
  • 将函数调用替换成函数本身,如果函数本身已经清楚易懂的话
  • 间接性可能带来帮助,但非必要间接性总是让人不舒服
  • 如果你需要重构一个函数,该函数内部又调用了一组不太合理的函数,可以将这些函数先内联到该函数中,再进行重构。

Inline Temp(内联临时变量)

提炼动机
  • 某个临时变量被赋予某个函数调用的返回值
具体做法
1
2
3
4
5
6
7
8
9
// before inline
double basePrice = anOrder.basePrice();
return (basePrice > 1000)
// after inline
return (anOrder.basePrice() > 1000)

Replace Temp with Query(以查询取代临时变量)

当函数中的临时变量保存了一组表达式的值,请将这段表达式代码提炼为一个新的函数。

提炼动机
  • 临时变量存储表达式的方式会使你的代码越来越长,因为这段代码是不能被复用的。
具体做法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// before
double getPrice() {
int basePrice = _quantity * _itemPrice;
double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}
// after
private int basePrice() {
return _quantity * _itemPrice;
}
private double discountFactor() {
if (basePrice() > 1000) return 0.95;
else return 0.98;
}
double getPrice() {
return basePrice() * discountFactor();
}

重构有啥用啊,真。。。真香。。。

Introduce Explaining Variable(引用解释性变量)

将复杂表达式中的某部分用临时变量来解释

提炼动机
  • 复杂的表达式难以阅读,临时变量可以帮助理解,特别是在条件判断表达式上。
具体做法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// before
double price() {
return _quantity * _itemPrice -
Math.max(0,_quantity - 500) * _itemPrice * 0.05 +
Math.min(_quantity * _itemPrice * 0.1, 100.0);
}
// after
double price() {
//可以提炼为方法,看复用情况
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(分解临时变量)

临时变量被赋值多次,但它既不是循环变量,也不是计算结果收集变量。

提炼动机
  • 临时变量被赋值多次,说明它承担了多个责任,应该被替换为多个临时变量,每个临时变量只承担一个责任。
  • 一个临时变量承担多件事情,会令代码阅读者懵圈。

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

在引用传递语言中,会改变该参数的值,在值传递语言中,不会影响外部继续使用该参数。

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

函数过大时,对其中局部变量的使用使你无法提炼出较小的函数

提炼动机
  • 局部变量太多,无法一一提取。
  • 当使用对象时,所有局部变量就会成为成员变量,方便拆解
具体做法
  • 新建一个类,将需要的局部变量申明为对象的属性
  • 在新类建立一个构造方法,参数为所有需要的局部变量
  • 将源方法的实现拷贝过来,然后使用新建类替换源方法

Substitute Algorithm(替换算法)