1. 问题及方案
- Long Methods(过长函数):Extract Method;
- 某些函数没做什么太多事情:Inline Method;
- 临时变量:Replace Temp with Query、Split Temporary Variable、Replace Method with Method Object;
- 参数问题:Remove Assignments to Parameters;
- 改进算法:Substitute Algorithm;
2. Extract Method(提炼函数)
将一段代码放进一个独立函数中,并让函数名称解释其用途。
2.1 动机
- 函数粒度小,易于复用和覆写;
- 高层函数更像注释;
2.2 做法
- 无局部变量:直接挪移
- 有局部变量,但不修改它或局部变量是对象:局部变量当做参数传递
- 对局部变量再赋值(Remove Assignments to Parameters)
- 若只有被提炼代码使用:挪移局部变量
- 否则:返回值
- 返回变量不止一个:推荐只返回一个值,使用多个函数;若编程语言支持“出参数”(output parameter),则可使用。(类似 C# 的 ref 和 out,可考虑新特性元组 Tuple)
详见书P133。
3. Inline Method(内联函数)
在函数调用点插入函数本体,然后移除该函数。
3.1 动机
- 短小函数只有一处调用,过多导致复杂性太高,移动不方便等等;
- 防止间接层过多,晕头转向;
3.2 做法
- 检查函数,确定不具备多态性
- 若子类继承该函数,就不能内联;
详见书P140
- 若子类继承该函数,就不能内联;
4. Inline Temp(内联临时变量)
将所有对该变量的引用操作,替换为对它赋值的那个表达式自身。
4.1 动机
- 临时变量被赋予某个函数调用的返回值。
ps
:个人感觉部分情况还是可使用临时变量,以便调试使用,更清晰。
5. Replace Temp with Query(以查询替代临时变量)
将这个表达式提炼到一个独立函数中,将这个临时变量的所有引用点替换为对新函数的调用。ps
:该操作会导致函数执行多次,故为了效率考虑,若执行速度慢的查询,还是需使用临时变量。
6. Introduce Explaining Variable(引入解释下变量)
将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。
6.1 动机
- 与 Inline Temp(内联临时变量)相反;
- 表达式可能非常复杂而难以阅读,临时变量可帮助拆分,提高阅读性;
ps
:作者推荐使用 Extract Method,以便复用。
7. Split Temporary Variable(分解临时变量)
针对每次赋值,创造一个独立、对应的临时变量。
7.1 动机
某个临时变量被赋值超过一次,它既不是循环变量,又不是搜集计算结果。除了这两种情况,其他意味着临时变量承担了多个责任,应该被替换为多个临时变量,每个变量只承担一个责任。
8. Remove Assignments to Parameters(移除对参数的赋值)
以一个临时变量取代该参数的位置。
8.1 动机
- 防止混用按值传递和按引用传递;
- 不要对参数赋值(特别是 Java、C# 这类只采用值传递的编程语言),出参数(ref、out)除外;
- 可以为参数加上 final 修饰符,强制不对参数赋值;
ps
:在按值传递的情况下,对参数的任何修改,都不会对调用端造成任何影响。
9. Replace Method with Method Object(以函数对象取代函数)
问题:你有一个大型函数,其中对局部变量的使用使你无法采用 Extract Method。
将这个函数放进一个单独对象中,这样该局部变量就成为了这个对象的字段,然后就可拆分。
10. Substitute Algorithm(替换算法)
将函数本体替换为另一个算法。
10.1 动机
解决问题有好几种方法,其中某些方法会比另一些简单。
使用这项重构手法之前,请先确定自己已经尽可能分解了原先函数。
参考
- 《重构-改善既有代码的设计》(第6章)