6.1 Extract Method(提炼函数)
将代码放进一个独立的函数中,并让函数名称解释该函数的用途
动机
提炼函数式最常用的手法之一.当我看见一个过长的函数或者一段需要注释才能让人理解用途的代码,我就会将这段代码放进一个独立的函数中.
保证函数名与函数体的语义距离最小.
做法
- 创造一个新函数,根据这个函数的亿图来对它命名.
- 将提炼的代码从原函数复制到昔年的目标函数中
- 仔细检查提炼的代码,看看其中是否引用了作用于限于源函数的变量.
- 检查是否有仅用于被提炼代码段的临时变量.如果有,在目标函数中将他们声明为临时变量.
- 检查被提炼代码段,看看是否有任何局部变量的值被它改变.
- 将被提炼代码段中需要读取的局部变量,当做参数传给目标函数
- 处理完所有局部变量之后,进行编译
- 在源函数中,将被提炼代码段替换为对目标函数的调用
- 编译,测试
6.2 Inline Method(内联函数)
在函数调用点插入函数本体,然后移除该函数
动机
非必要的间接性让人不舒服.
做法
- 检查函数,确定它不具有多态性.(如果子类继承了这个函数就不要将此函数内联,因为子类无法覆写一个根本不存在的函数)
- 找出这个函数所有被调用点
- 将这个函数的所有被调用点都替换为函数本体
- 编译,测试.
- 删除该函数的定义
6.3 Inline Temp(内联临时变量)
将所有对改变了的引用动作,替换为对它赋值的那个表达式自身
动机
发现某个临时变量被赋予某个函数调用的返回值.一般来说,这样的临时变量不会有任何危害,可以放心地把它留在那儿.但如果妨碍了其他重构手法,就应该将他内联
做法
- 检查给临时变量赋值的语句,确保等号右边的表达式没有副作用
- 如果这个临时变量并未被声明为final.就将他声明为final
- 找到该临时变量的所有引用点,将他们替换为 为临时变量赋值 的表达式
- 每次修改后,编译并测试
- 修改完所有引用点之后,删除该临时变量的声明和赋值语句
- 编译.测试
6.4 Replace Temp with Query(以查询取代临时变量)
你的程序以一个临时变量保存某一表达式的运算结果
将这个表达式提炼到一个独立函数中将这个临时变量的所有引用点替换为对新函数的调用.伺候,新函数就可被其他函数使用
动机
临时变量的问题在于: 他们是暂时的,而且只能在所属函数内使用.如果把临时变量替换为一个 查询 ,name同一个勒种的所有函数都将可以获得这份信息.
做法
首先简单情况
- 找出纸杯赋值一次的临时变量(超过一次就好考虑使用Split Temporary Variable将他分割为多个变量)
- 将该临时变量声明为final
- 编译
- 将对该函数临时变量赋值 之语句的等号右侧部分提炼到一个独立函数中
- 编译,测试
- 在该临时变量身上试试Inline Temp
6.5 Introduce Explaining Variable(引入解释性变量)
你有一个复杂的表达式
将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途.
动机
表达式可能非常复杂而难以阅读.语义变量可以解释条件逻辑
做法
- 声明一个final 临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给他
- 将表达式中的 运算结果这一部分,替换为上述临时变量.(如果被替换的这一部分在代码中重复出现,你可以每次一个,逐一替换)
- 编译,测试
- 重复上述过程
6.6 Split Temporary Variable(分解临时变量)
你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果.
针对每次赋值,创造一个独立,应对的临时变量.
动机
临时变量有各种不同用途,其中某些用途会很自然地导致临时变量被多次赋值.如果临时变量承担多个责任,他就应该被替换(分解)为多个临时变量,每个变量只承担一个责任.
做法
- 待分解临时变量的声明机器第一次被赋值处,修改其名称.
- 将新的临时变量声明为final.
- 以该临时变量的第二次赋值动作为界,修改此前对该临时变量的所有引用点,让他们引用新的临时变量.
- 在第二次赋值处,重新声明原先那个临时变量
- 编译,测试
- 主次重复上述过程.每次都在声明处对临时变量改名,并修改下次赋值之前的引用点
6.7 Remove Assignments to Paramters(移除对参数的赋值)
代码对一个参数进行赋值
以一个临时变量取代该参数的位置
动机
对参数赋值会降低代码的清晰度,而且混用了按值传递和引用传递这两种参数传递方式.Java只采用按值传递方式
做法
- 建立一个临时变量,把待处理的参数赋予它
- 一队参数的赋值为界,将气候所有对此参数的引用点,全部替换为对 次临时变量的引用
- 修改赋值语句,时期改为对新建之临时变量赋值
- 编译,测试
6.8 Replace Method with Method Object (以函数对象取代函数)
你有一个大型函数,其中对局部变量的使用使你无法采用Extract Method
将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段.然后你可以在同一个对象中将这个大型函数分解为多个小型函数
动机
我在本书中不断强调小型函数优美动人.只要将相对独立的代码从大型函数中提炼出来,就可以大大提高代码的可读性.
但局部变量的存在会增加函数分解难度,如果一个函数之中局部变量泛滥成灾,name想分解这个函数是非常困难的.Replace Temp with Query可以助你减轻这一负担,但有时候你会发现根本无法拆解一个需要拆解的函数,这种情况你应该吧手伸进工具箱的深处,祭出函数对象(method object)[Beck]这件法宝.
做法
- 建立一个新类,根据待处理函数的用途,为这个类命名
- 在新类中建立一个final字段,用以保存原型大型函数所在的对象.我们将这个字段称为源对象.同时针对源函数的每个临时变量和每个参数在新类中建立一个对应的字段保存之.在新类中建立一个构造函数,接收源对象及原函数的所有参数作为参数
- 在新类中建立一个compute函数
- 将原函数代码复制到compute函数中.如果需要调用源对象的任何函数,请通过源对象字段调用
- 编译
- 将旧函数的函数本体替换为这样一条语句: 创建上述新类的一个新对象,而后调用其中的compute 函数.
6.9 Substitute Algorithm(替换算法)
你想要把某个算法替换为另一个更清晰的算法.
将函数本体替换为另一个算法
动机
解决问题有好几种方法,我敢打赌其中某些方法会比另一些简单.算法写时如此.
如果你发现做一件事可以有更清晰的方式,就应该以比较清晰的方式取代复杂的方式.重构可以把一些复杂东西分解为较简单的小块,但有时你就是必须壮士断腕,删掉整个算法,代之以较简单的算法.随着对问题有更多的理解,你望望会发现,在原先的做法之外,有更简单的解决方案,此时你就需要改变原先的算法.
做法
- 准备好另一个(替换用)算法,让他通过编译
- 针对现有测试,执行上述的新算法.如果结果与原本结果相同,重构结束.
- 如果测试结果不同于原先,在测试和调试过程中,以旧算法为比较参照标准
- 本文链接: https://halo.cjh.kim/archives/重构改善既有代码的设计6
- 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!