重构方法列表

以前一直觉得重构是一种很高大上的事情,但最近在看了《重构》这本书后明白这是每一个程序员都应该掌握的很基础的技能,我们应该将它融入自己的骨血,以后再需要时可以向喝水呼吸一般自然的使用它。为了方便以后时常查阅,在这里将里面的作者总结的重构方法列表记录一下。

重新组织函数

  • 提炼函数(Extract Method)

当我们遇到过长函数或者需要用注释来说明的代码,我们应该将里面的相关代码提炼出来,形成一个新的函数,并用函数名来说明它的用途,哪怕它可能只有一行代码。

  • 内联函数(Inline Method)

这与提炼函数相反,即如果这个函数体可以和函数名一样清晰易懂,那么就应该将它放回到原先的函数中。这种情况一般是我们对这个提炼出的函数进行了进一步的提炼,使它变得太多清晰,已不需要单独称为函数;还有一种情况是我们之前的重构并不合理,将这个函数放回原来的函数中进行重新提炼。

  • 内联临时变量(Inline Temp)

在我们进行函数提炼时,往往遇到最大的问题是对临时变量的处理。函数中存在太多的临时变量会造成代码混乱,不易阅读,同时会对我们提炼函数造成很大阻碍,在提炼函数之前我们往往需要通过一些方法来减少函数中临时变量。

这个方法是说如果你有一个临时变量,只被一个简单表达式赋值过一次,并且它妨碍了其他的重构方法,比如提炼函数,那么就应该用这个简单表达式来替换这个变量。

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

你的程序用一个临时变量来保存某一表达式的运算结果,那么将这个表达式提炼成一个独立函数,在使用这个临时变量的地方替换它。

其实一般我们都喜欢用一个临时变量来某个函数的运算结果,所以这些重构列表只是建议而不是法则,我们可以根据当时的情况,甚至是自己的喜好来做出抉择。如果这个临时变量妨碍你对当前函数的重构,那么不妨采用这个方法来取代它;但这也会有个问题,即会多次重新运算这个函数,如果它只做了简单运算,那无所谓;而如果这个函数中的运算很耗时,那么我们就不应该这样重构它。

  • 引入解释型变量(Introduce Explaining Variable)

使用临时变量来替代复杂表达式。这项重构方式常用的地方是提炼条件子句,以一个良好的命名来解释该条件子句的意义(我曾见过用类变量来替换的情况,这无疑会给我们带来很大方便,但我们确实应该尽量避免这种做法,因为这会给类带来污染,当然如果这个类的职责便是如此,那就另当别论,比如下文的函数对象);也可以一些较复杂的算法函数中,以一个临时变量来解释某一步运算的意义。

这个重构方式一般很少使用,我们一般使用一个有着良好命名的函数来代替它,毕竟它引入了临时变量,而我们之前说过过多的临时变量会造成混乱,并对后续的重构造成不利影响。

  • 分解临时变量(Split Temporary Variable)

如果程序中有这么一个临时变量,它被多次赋值,但是它并不扮演中循环变量或者收集计算结果的角色,那么将它分解成多个独立的变量,用多个良好的变量名来解释它每一步的作用。一个变量不应该承担多个责任。

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

这个重构方式告诉我们不应该对传入函数的参数进行赋值,这会让人困惑(特别是在一些存在按引用(指针)传递的语言中,当然java中只有按值传递)。传入的参数应该仅仅只扮演“被传递进来的东西”这个角色,当需要对某个参数进行重新赋值时,我们可以采用一个临时变量来暂存该参数的值。

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

存在这样一个大型函数,里面的临时变量很多致使我们无法很好地提炼函数,那么我们就可以采用这个重构方式,将整个函数放在一个独立的对象中,将里面的临时变量变成类的属性变量,然后我们就可以在这个对象中将这个大型函数提炼成多个小型函数。

  • 替换算法(Substitute Algorithm)

采用一个更简单清晰的算法来替换原来的复杂的算法,有时随着对问题的了解更深(比如需求),我们发现可以采用一个更简单的方式来代替原先复杂的方案,这是我们可能就需要壮士断腕,抛弃原先整个算法,而采用当前更简单的来替代它。

当然这时候我们需要考虑整个项目的情况,如果我们判定这个地方在之后的迭代中基本上不需要被修改,那么不替换它也无妨。但是如果发现它之后可能会改变,那采用一个更简单的方案会让我们后续的修改更轻松一些。

在对象之间搬移特性