代码中的坏味道-重构改善既有代码的设计

3.1 重复代码(如果需要修改该段代码,那么需要修改很多地方,容易导致问题。不易维护。)

如果一个类的两个函数有相同的表达式,可以将表达式提炼成函数,两个地方分别调用这个函数。

如果是两个子类中有相同表达式,将表达式提炼成函数,推入超类。如果代码只是类似,提炼函数的时候需要将相似部分和差异部分分割,构成一个单独函数。其中可能可以运用塑造模板函数或这个模板方法设计模式。

如果有些函数以不同算法做相同的事情,可以选择较清晰的一个,使用替换算法将其替换掉。

如果是两个毫不相干的类出现重复代码,可以考虑对其中一个提炼成新类,另一个类调用这个新类。

3.2 过长函数 (函数过长一般说明函数做了太多的事情,违背了单一职责原则。将函数分解成更细粒度的函数,易于改造和使用。)

每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并用其用途命名。(代码比较难懂的时候)

百分之九十九的场合,要把函数变小,只需要使用提炼函数找到函数适合集中在一起的部分(除了提炼函数,还需要考虑函数存放的位置)

如果提炼函数的新函数中内存在大量临时变量和参数,可以通过用查询替代临时变量来消除临时元素(如果查询不耗费资源和时间,一般来说创建临时变量需要分配额外的地址,创建太多临时变量会浪费很多系统资源,用查询替换临时变量会节省地址空间。当然不是说完全不能使用临时变量,如果使用临时变量可以方便理解代码也可以使用,但是慎用。太多临时变量可能会导致问题)。也可以使用引入参数对象,或者保持对象完整来使过长的参数列变得简洁一些。

如果做了上述操作之后还是有太多临时变量和参数,那就应该使用函数对象替代函数。

那些部分需要提炼

一、寻找注释,有注释的地方一般说明这个地方不好理解。而不好理解的原因很可能是做了额外的事情。这就需要提炼出来

二、条件表达式和循环。条件表达式可以通过分解表达式来处理。循环和循环内的代码需要提炼成函数。

3.3 过大的类 (如果类需要做的事情很多,容易出现大量实例变量,那么紧接着就会出现大量重复的代码)

将几个变量提炼到新类,提炼的时候尽量选择相关的变量。

如果一个类有太多代码,往往适合使用提炼类或者提炼子类。这里有个技巧,先确定客户端如何使用,然后运用提炼接口为每一种方式提炼出一个接口。

3.4 过长的参数列(参数过多,函数使用成本变高,因为如果没有函数需要的参数就无法调用函数)

如果参数大部分来自一个对象,那么可以直接传入这个对象。从对象里获取参数。如果可以通过给对象发送请求可以获取参数,那么应该使用函数替换参数。使用保持对象完整将来自同一个对象的一堆数据收集起来,用该对象替换他们。如果数据缺乏合理的对象归属,可以使用引用参数对象来为他们创建一个”参数对象“

3.5 发散式变化 (将分裂成多个类,每个类处理对应的变化)

针对某一外界变化的所有相应修改,应该发生在单一类中。新类的所有内容都应该反应此变化。应该找出某特定原因造成的所有变化,然后运用提炼类将其提炼到新类。

3.6 霰弹式修改(将变化统一到同一个地方,统一修改,方便维护)

和发散式变化相反,如果遇到某个变化,需要修改的代码分布在四处,很难修改,而且容易忘记。应该使用移动函数和移动字段把所有修改的代码放到同一个类中,没有合适的类就创建一个。可以运用将类内联化将一系列行为放进同一个类。

发散式变化指一个类受多种变化影响,霰弹式修改指一种变化引发多个类修改。

3.7 依恋情结(将相关的类和函数放到一起,更容易管理)

函数对某个类的兴趣高过自身所在的类,某个函数调用其他类的大部分对象。需要将该函数移动到这个类中。如果只有一部分,那么将其提炼成函数再移动。

判断哪个类拥有最多被此函数使用的数据,然后将这个函数放到该类中

3.8 数据泥团 (数据间有依赖关系,创建新对象更容易管理)

两个类中相同的字段,许多函数签名中有相同的参数,这些总是绑在一起的数据应该有自己的对象。

删除众多数据的一项,其他数据有没有失去意义,如果不再有意义,那么就应该为他们产生一个新的对象。(说明这些数据之间有依赖关系)

3.9 基本类型偏执 (不要偏执于使用基础类型,有时需要创建新的数据类型)

如果有一组应该总是放在一起的字段,运用提炼类。如果参数列中有基本型数据,可以使用引用参数对象,或者使用对象替换数组。

3.10 switch语句 (使用多态替换switch)

使用提炼函数将switch语句提炼到独立函数,将函数移动到需要多态性的类中。

3.11 平行继承体系(霰弹式修改的特例)

每当你为某个类增加一个子类,也必须为其他的类增加一个子类。让其他类的实例引用另一个继承体系的实例。

3.12 冗赘类 (去掉没有作用或者作用很小的类,可以使代码更清晰,简洁)

如果某些子类没有做足够的工作,可以试试折叠继承体系。对于几乎没用的组件,可以使用将类内联化。

3.13 夸夸其谈未来性 (为了可维护性而做了一些操作,但是却没有用到,个人感觉和冗赘类类似)

如果类和函数的唯一用户时测试用例,那么可以将其和测试用例一起删掉。

3.14 令人迷惑的临时字段 (和过长参数列中临时变量太多类似,太多临时变量会让人难以理解)

对象内的某个实例变量仅为某种特定情况而设,这样的的代码让人不易理解。可以使用提炼类存放这些变量。

过长参数列中参数中临时变量太多也属于这种情况

3.15 过度耦合的消息链 (消息链中间有地方需要修改,就不易维护)

一个对象请求另一个对象,另一个对象再请求其他的对象。这就是消息链。

先观察消息链最终得到的对象时用来干什么的,能否以提炼函数把使用对象的代码提炼到一个独立函数,再运用移动函数将其推入消息链。

3.16 中间人 (太多无用委托会导致类关系变得复杂)

使用删除中间人去掉过度使用的委托。

3.17 狎昵关系 (类太亲密意味这某个类修改会影响另一个类)

两个类太多亲密,可以使用移动函数和移动字段划清界限。

3.18 异曲同工的类 (重复的工作只需要做一次)

如果两个类做一样的事情,但是名称不同,那么去掉其中一个类,根据用途给另一个类改名。

3.19 不完美的类库 

如果只想修改库类的一两个函数,可以运用引入外部方法。如果想添加一堆额外行为,运用引入本地扩展。

3.20 数据类 

拥有一些字段,以及用于访问这些字段的函数,除此之外没有作用

3.21 被拒绝的遗赠

如果子类复用了父类的行为,但是不支持父类的接口。用委托取代继承来修改这个子类。

3.22 过多的注释

 如果需要注释一块代码可以考虑提炼函数。如果已经提炼,但是还是需要注释,那么可以考虑重命名函数。如果需要注释说明系统的需求规格试试导入断言。

posted @ 2019-07-21 11:50  菲菲菲菲菲常新的新手  阅读(325)  评论(0编辑  收藏  举报