《重构》读书笔记——代码的坏味道(重复代码)

重复代码
如果在一个以上的地方看见相同的程序结构,那么合而为一,是比较好的选择
1、同一个类的两个函数含有相同的表达式——使用 “提炼函数”的解决办法
2、两个互为兄弟的子类内含有相同的表达式——先使用 “提炼函数”,然后再对被提炼出来的代码使用 “函数上移”,将它推入超类。如果代码只是类似而不是相同,那么就使用 “提炼函数”,将相似的和差异部分分隔开,构成单独的函数。然后你可以使用 “塑造模版函数”获得一个模版方法设计模式,如果有些函数以不同的算法做相同的事情,可以选择较清晰的一个,使用 “替换算法”将其他替换掉。
3、如果两个毫不相干的类出现重复代码,该考虑使用 “提炼类”,将重复的代码提炼到一个独立的类中,然后在另一个类使用这个新类

提炼函数
1、创建一个新函数,根据意图对它命名
2、将提炼出的代码从源函数复制入目标函数
3、仔细检测提炼的代码,看其中是否有“作用于仅限于源函数”的变量(包括局部变量和源函数参数)
4、检查任何局部变量的值是否被改变,如果被改变看看能否作为一个查询,然后将结果返回。如果很难这么做,
那么使用 “分解临时变量”或者 “用查询代替临时变量”来解决
5、将提炼代码段中需要读取的局部变量,当作参数传给目标函数
6、处理完局部变量,进行重新编译
7、在源函数中,将提炼代码段替换为对目标函数的调用

分解临时变量:如果一个临时变量被赋值多次,那么说明它承担了多个责任(循环变量和结果收集变量除外),同一个临时变量承担多个责任会让人糊涂,所以需要分解成多个临时变量
1、在待分解临时变量和第一次被赋值处,修改其名称(注意,如果是i=i+某表达式的形式则不要分解它,一般为结果收集变量,通常作用是累加,字符串接合,写入流或者集合添加元素)
2、将新的临时变量声明为final
3、以该临时变量的第二次赋值动作为界,修改此前所有对临时变量的引用点,让他们引用新的临时变量
4、在第二次赋值处,重新声明原先那个临时变量
5、编译,测试
6、逐次重复上述过程,每次都在声明处对临时变量改名,并修改下次赋值之前的引用点

用查询代替临时变量:将临时变量变为一次函数查询,可以简化函数,编写更清晰的代码
1、找出只被赋值一次的临时变量(如果被多次赋值,考虑使用 “分解临时变量”
2、将临时变量声明为final
3、编译(确保只被赋值一次)
4、将“对该临时变量的赋值表达式”提炼到一个函数当中(首先声明为private,然后再放宽限制。确保提炼的函数没有副作用,即不修改任何对象的内容,否则使用 “将查询函数和修改函数分离”
5、编译,测试
6、在该临时变量上实施 “内联临时变量”

将查询函数和修改函数分离:某个函数既返回对象状态值,有修改对象状态,那么建立两个不同的函数,其中一个负责查询,另一个负责修改
1、新建一个查询函数,令它的返回值与原函数相同(如果返回临时变量,那么找出临时变量的位置)
2、修改原函数,令它调用查询函数,并返回获得的结果(每个return语句都应该返回return newQuery())
3、编译测试
4、将调用原函数的代码改为调用查询函数,然后,在调用查询函数的那一行之前,加上对原函数的调用,每次修改后,编译并测试
5、将原函数的返回值改为void,并删除所有的return语句。

内联临时变量:你有一个临时变量只被简单表达式赋值一次,而它妨碍了其他重构手法,将所有对该变量的引用动作,替换为对它赋值的表达式本身。一般和用查询代替临时变量一起使用
1、检查给临时变量赋值的语句,确保等号右边的表达式没有副作用。
2、将这个临时变量声明为final,然后编译(确保这个临时变量只被赋值一次)
3、找到所有的临时变量引用点,将其替换为表达式。
4、每次修改后,编译并测试。
5、修改完所有的引用点后,编译,测试。

函数上移:对于在各个子类中某个函数完全相同的情况,将该函数移至父类
1、检查待提升的函数,确定它是完全一致的(如果不一致可使用 “替换算法”让它们一致)
2、如果待提升的函数签名不同,将那些签名都改成你想在超类中使用的签名
3、在超类中新建函数,将函数内容复制到其中,做适当调整然后编译(如果待提升的函数使用了一个子类的字段,可以使用 “字段上移”或者使用 “自封装字段”,然后在超类中把取值函数声明为抽象)
4、逐一移除所有的子类函数,每次移除都要编译测试
5、观察函数调用者,是看看是否可以改为使用超类类型的对象。

替换算法:把某个算法替换为另一个更清晰的算法
1、准备好另一个算法,让它通过编译
2、针对现有测试,执行上述新算法,如果结果和原本结果相同,重构结束
3、如果结果不相同,在测试和调试过程中,以旧算法为比较参照标准

字段上移:两个子类拥有相同的字段,将该字段移植超类
1、针对待提升的字段,检查它们的所有被使用点,确认它们以相同的方式被使用
2、如果这些字段名称不同,先将他们改名,改作你超类里面想取的名字
3、编译,测试
4、在超类中新建一个字段(如果是private的,则声明为protected)
5、移除子类字段,编译测试
6、考虑对超类的新建字段使用 “自封装字段”

“自封装字段”:你直接访问一个字段,但字段之间的耦合关系逐渐变嘚笨拙,为这个字段设置get/set函数,并且只以这些函数来访问字段。
1、为待封装的字段建立get/set函数
2、找出该字段的所有引用点,将它们全部改为调用get/set函数(可以将该字段改名,让编译器帮你找出所有引用点)
3、将字段声明为private
4、复查,确保已经找出所有引用点
5、编译测试

“塑造模版函数”:你有一些子类,其中相应的某些函数具有相同顺序执行的操作,但操作细节有所不同,将这些操作分别放进独立函数中,并保持它们相同的签名,于是原函数也相同,将原函数上移至超类。
1、在各个子类中分解目标函数,是分解后的各个函数要不完全相同,要不完全不同
2、运用 “函数上移”将各个子类内完全相同的函数上移至超类
3、对于剩下的完全不同的函数,实施 “函数改名”,是所有的这些函数签名完全相同
4、修改上述签名后,编译并测试
5、运用“函数上移”将所有原函数逐一上移到超类,在超类中将哪些代表不同操作的函数定义为抽象函数
6、编译测试
7、移除其他子类中的原函数,逐一编译测试

“函数改名”:函数的名称未能揭示出函数的用途,修改函数名称
1、检查函数名是否被超类或者子类实现过,如果是,则针对每份实现分别进行一下步骤:
2、声明一个新函数,将它命名为你想要的新名称。将旧函数的代码复制到新函数,并适当调整
3、编译
4、修改旧函数,令它将调用转发给新函数。
5、编译,测试
6、找到所有旧函数的引用点,修改它们,令它们改用新函数,每次修改必须编译测试
7、删除旧函数
8、编译测试

提炼类:某个类做了应该由两个类做的事情,建立一个新类,将相关的字段和函数从旧类搬到新类
1、决定如何分解类所承担的责任
2、建立一个新类,用以表现从旧类中分离出来的责任(如果旧类剩下的责任与名称不符合,为旧类更名)
3、建立“从旧类访问新类”的连接关系
4、对于你想搬运的每一个字段,用 “搬运字段”来搬运它
5、每次搬运后,编译测试
6、使用 “搬运函数”将必要的函数移至新类,先移动比较低层的函数(也就是被其他调用多于调用其他的函数),再搬运高层函数
7、每次搬运之后,编译测试
8、检查,精简每个类的接口
9、决定是否需要公开新类,如果需要公开,就要决定让它成为引用对象还是不可变值对象

搬运字段:你的程序中,某个字段被其所在的类之外的另一个类更多的用到,在目标类中新建一个字段,修改源字段的所有用户,令它们改用新字段
1、如果字段的访问级是public,使用 “封装字段”将它封装起来(如果你有可能移动哪些频繁访问的字段或者有很多函数访问,先使用 “自封装字段”
2、编译,测试
3、在目标类中建立与源字段相同的字段,并同时建立相应的设值/取值函数
4、编译目标类
5、决定如何在源对象中引用目标对象
6、删除源字段
7、将所有对源字段的引用替换为对某个目标函数的调用
8、编译测试

搬移函数:在该函数最常引用的类建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数或者将旧函数移除。
1、检查源类中被源函数所使用的一切特性(包括字段和函数),考虑它们是否也该被搬移(如果某个特性只被你打算搬移的函数用到,就该把它们一并搬移)
2、检查源类的子类和超类,看看是否有该函数的其他声明(如果出现其他声明,你无法搬移,除非目标类也表现出同样的多态性)
3、在目标类中声明这个函数(你可以为此函数重新取一个名字)
4、将源函数的代码复制到目标函数中。调整后者,使其能在新类中正常运行(如果目标函数使用了源类的特性,你得决定如何从目标函数引用源对象,如果没有相应的引用机制,则把源对象的引用当作参数传递)
5、编译目标类
6、决定如何从源函数正确引用目标对象(可能会有一个现成的字段或者函数帮助你取得目标对象,如果没有,就看能否轻松建立一个这样的函数,如果还是不行,就在源类中新建一个字段来保存目标对象,这样可能是一个永久性修改,但你也可以让它是暂时的)
7、修改源函数,使之成为一个纯委托函数
8、编译测试
9、决定是否删除函数,或者将它作为一个委托函数保留下来(如果你经常要在源对象中引用目标函数,那么作为委托函数保留会比较简单)
10、如果要移除源函数,请将源类中对源函数的所有调用,替换为对目标函数的调用
11、编译测试

封装字段:将原来的public字段声明为private,并提供相应的访问函数
1、为public字段提供set/get函数
2、找到这个类以外使用该字段的所有地点。如果客户只是读取了该字段,就把引用替换为对取值函数的调用;如果用户修改了该字段,就将此引用替换为对设值函数的调用
3、每次修改后,编译测试
4、将字段的所有用户修改完毕后,把字段声明为private
5、编译测试
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值