03.重构-重新组织函数

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章)
坚持原创技术分享,您的支持将鼓励我继续创作!