关于重构,代码的坏味道,应该重构的代码
代码坏味道:是指在代码之中潜在问题的警示信号。并非所有的坏味道所指示的确实是问题,但是对于大多数坏味道,均很有必要加以查看,并作出相应的修改。
1. 重复的代码
如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好。
- 同一个class内的两个函数中含有重复的代码段
- 两个兄弟class的成员函数中含有重复的代码段
- 两个毫不相关的class内出现重复的代码段
注意:重复的代码是多数潜在BUG的温床!
2. 过长的函数
拥有短函数的对象会活的比较好、比较长。
- 程序愈长就愈难理解
- 函数过长阅读起来也不方便
- 小函数的价值:解释能力、共享能力、选择能力
原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立的函数中。记着,起个好名字!
3. 过大类
如果想利用单一类做太多事情,其内往往就会出现太多的成员变量。
- 提取完成同一任务的相关变量到一个新的类
- 干太多事情的类,可以考虑把责任委托给其他类
注意:一个类如果拥有太多的代码,也是代码重复、混乱、死亡的绝佳滋生地点。
4. 过长的参数列表
太长的参数列表难以理解,太多参数会造成前后不一致、不易使用,而且你需要更多数据时,就不得不修改它。
原则:参数不超过3个!
5. 发散式变化
我们希望软件能够更容易被修改。一旦需要修改,我们希望能够跳到系统的某一点,只在该处做修改。如果不能做到这点,你就嗅出“坏味道:发散式变化”或“坏味道:霰弹式修改”。
发散式变化:一个类受多种变化的影响
- 数据库新加一个字段,同时修改三个函数:Load、Insert、Update
- 新加一个角色二进制,同时修改四处
- …
原则:针对某一外界变化的所有相应修改,都只应该发生在单一类中
6. 霰弹式修改
如果每遇到某种变化,你都必须在许多不同的类内做出许多小修改以响应之。如果需要修改的代码散布四处,你不但难以找到它们,也很容易忘记某个重要的修改。
霰弹式修改:一种变化引起多个类相应的修改
7. 依恋情节
函数对某个类的兴趣高过对自己所处类的兴趣,就会产生对这个类的依恋情节,造成紧耦合。
原则:判断哪个类拥有最多被此函数使用的数据,然后将这个函数和那些数据摆在一起。 原则:将总是变化的东西放在一块。
8. 数据泥团
有些数据项,喜欢成群结队地待在一块。那就把它们绑起来放在一个新的类里面。这样就可以:
- 缩短参数列表
- 简化函数调用
9. 基本型别偏执
代码中有很多基本数据类型的数据。
原则:如果看到一些基本类型数据,尝试定义一种新的数据类型,符合它当前所代表的对象类型。
10. switch惊悚现身
面向对象程序的一个最明显特征就是:少用switch语句。从本质上说,switch语句的问题在于重复。
原则:看到switch你就应该考虑使用多态来替换它。
11. 冗赘类
你所创建的每一个类,都得有人去理解它、维护它,但一个类没有存在的必要时候,就让这个类庄严扑义吧!
原则:如果一个类的所得不值其身价,它就应该消失。
12. 夸夸其谈其未来性
对未来不可预知的变化考虑的过多,造成系统更难理解和维护。如果应对变化的代码都会被用到,那是值得的那么做;如果用不到,就不值得。
原则:代码应该满足当前的需求,并留有可扩展的余地。对于未来的变化,既不要考虑的太多,也不能一点都不考虑。
13. 令人迷惑的暂时成员变量
有时你会看到这样的对象:其内某个成员变量仅为某种特定的情形而设。这样的代码容易让人不解,因为你通常认为对象在所有时候都需要它的所有变量。
在变量未被使用的情况下猜测当初设置目的,会让你发疯。
14. 无用的中间人
过度使用委托。你也许会看到某个类的接口有一半的函数都委托给其他类,这样就过度运用了。所以,删除无用的中间人。
15. 狎昵关系
有时你会看到两个类过于亲密,花费太多时间去探究彼此的private成分。
原则:过分狎昵的类必须拆散。
16. 异曲同工的类
如果两个函数做同一件事情,却有着不同的签名式。
原则:删除一个,保留一个。
17. 不完美的程序库类
库的设计有时会不够好,不那么容易使用,可能还会有那么一点小缺陷。
工具:
- Introduce Foreign Method
- Introduce Local Extension
18. 过多的注释
过多注释的代码段,往往都是因为那段代码比较糟糕,散发着一股恶臭。
原则:当你感觉需要写注释时,请尝试重构,试着让所有注释都变得多余。
----------------------------------------------------------------------------------------------------------------------------
应该重构的代码
1.重复的代码:
重复代码在同一个类中的不同方法中,则直接提炼为一个方法
如果重复代码在两个互为兄弟的子类中,则将重复的代码提到父类中
如果代码类似,则将相同部分构成单独函数,或者用 Template Method 设计模式
重复代码出现在不相干的类中,则将代码提炼成函数或者放在独立的类中
2.过长的函数:
降低了可读性,应该将独立的功能提炼成新函数
3. 过大类
使得责任不清晰,容易造成重复代码,混乱,应该将过大类的功能拆分成多个功能单一的小类
4.过长的参数列
过长的参数列难以理解,而且容易传错参数。应该将参数列表用参数对象替换
5.发散式变化:
一个类由于不同的原因而被修改。应该将类拆分成多个,每个类只因为一种变化而修改。
6.霰弹式修改:
与发散式变化相反,遇到变化时需要修改许多不同的类。应该将类似的功能放到一个类中
7.依恋情结:
函数对某个类的兴趣高过对自己所处的类,通常是为了取其他类中的数据。应该将函数部分功能移到它感兴趣的类中
8.数据泥团:
在多个地方看到相同的数据项。例如:
多个类中相同的变量,多个函数中相同的参数列表,并且这些数据总是一起出现。应该将这些数据项放到独立的类中
9.基本类别偏执:
对象技术的新手通常不原因在小任务上运用小对象,比如结合数值和币别的money class,等,应该Replace Data Value with Object。
10.分支语句:
大量的分支、条件语句导致过长的函数,并且可读性差。应将它变成子类或者使用 State和 Strategy模式
11.平行继承体系
是霰弹式修改的特殊情况。一般是当你为某个类增加了一个子类,必须也为另一个类相应的增加一个子类。如果你发现某个继承体系的class名称前缀和另一个继承体系的class名称前缀完全相同。变素有问题了。 应该让一个继承体系的实体(instance)指涉(参考,引用,refer to)另一个继承体系的实体。
12 冗赘类(lazy class)
几乎没有用的组建 应该进行inline class
13.夸夸其谈未来性
现在用不到,觉得未来可以用到的代码,要警惕。应该将用不上的代码去掉
14.过度耦合的消息链
一个对象请求另一个对象,后者又请求另外的对象,然后继续。。。。,形成耦合的消息链。应该公布委托对象供调用
15 纯粹的数据类
将数据类中数据以Public方式公布,没对数据访问进行保护。应该 将数据封装起来,提供Get/Set方法。
16 过多的注释
代码有着长长的注释,但注释之所以多是因为代码很糟糕。先重构代码,再写上必要的注释
17 令人迷惑的暂时值域
如某个instance变量仅为某特定情况而设,在变量未被使用的情况下猜测当初设置目的非常困难。应该建立一个新的class,把所有和这个变量相关的代码都放到里面。