前言:本文只摘要了24种代码「坏味道」最核心的要点,也不准备贴代码示例,是因为Sheldon自己在写的过程中,自然就想起了很多自己写的代码「坏味道」。所以希望大家在看文章的过程中多回忆自己的代码,而不是被冗长的文字和大量代码示例分散注意力。
1. 神秘命名
命名是编程中最难的两件事之一。正因为如此,修改命名可能是最常用的重构方法。
如果你发现改名很难,那就说明代码设计有问题。
当我们不能给一个模块,一个对象,一个函数,甚至一个变量找到合适名称的时候,往往说明我们对问题的理解还不够透彻,需要重新去挖掘问题的本质,对问题域进行重新分析和抽象。
2. 重复代码
同一类的两个函数含有相同的表达式,就应该提炼。
3. 过长函数
活得最长,最好的程序,其中函数一般都很短。
如果你觉得需要写注释,大部分情况就代表这个东西需要写进一个独立的函数里面,然后根据用途来命名比较好。
条件表达式和循环往往也是提炼函数的信号。
4. 过长参数列表
使用类可以有效的缩短参数列表。如果多个函数有同样的几个参数,引入一个类就尤为有意义。
5. 全局数据
全局数据仍然是最刺鼻的坏味道之一。它的问题是,全局数据在任何地方都可以被修改。
所以正确的做法是将全局数据封装起来,用函数将其包起来,这样就知道那些地方修改了它。
有少量的全局数据或者无妨,但数量越多,处理难度就会指数上升。(良药与毒药的区别在于剂量)
6. 可变数据
核心是缩小作用域。
可以通过封装变量来确保所有数据更新操作都通过很少几个函数来进行,使其更容易被监控。
7. 发散式变化
发散式变化指某个模块因为不同的原因在不同的方向上发生变化。
每次只关心一个上下文。
找到引起发散式变化的原因,将它拆分出来。
8. 霰弹式修改
在每次修改的时候,应该只修改一处,而不是到处的修改。因为一个需求,需要修改3处代码,那么这就需要思考,这3处代码是否应该抽离出来。
一个常用的策略就是使用内联(inline)重构代码把本不该分散的逻辑拽回一处。
9. 依恋情结
模块化,力求代码分出区域,最大化区域内部交互,最小化区域间交互。
如果两个模块交互频繁,它们应该合并在一起。
10. 数据泥团
如果在多个类中,出现了很多相同项的数据,你需要想想是否要通过将数据提炼成类,来抽离出一个独立对象。
建议新建类而非简单的结构体。
11. 基本类型偏执
很多程序员不愿意创建对自己的问题域有用的基本类型,如钱,坐标,范围等。
比如有程序员用字符串来表示电话号码,实际上你应该抽象出来一个电话号码对象。
12. 重复的switch
尽量使用多态而非switch。
13. 循环语句
我们应该用管道操作(如filter和map)来替代循环,这样能更快的看清被处理的元素和处理他们的动作。
14. 冗赘的元素
能简单的代码,尽量简单。未来变复杂的时候,再去考虑它。
15. 夸夸其谈的通用性
同上,能简单的代码,尽量简单。通用性?过早的优化是万恶之源
16. 临时字段
临时字段指内部某个字段仅为某种特定情况而设。
临时的字段不应该存在。你需要给他们搬个新家,把所有和临时变量相关的代码搬至那里。
17. 过长的消息链
如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象,这就是消息链。
消息链意味着客户端会耦合消息链的查找过程。应该将查找过程独立出一个函数。
18. 中间人
委托函数过多时,减少委托,移除中间人,让调用者直接访问目标类进行操作。
19. 内幕交易
减少模块之间频繁的数据交换,并把这种交换放到明面上。
20. 过大的类
当一个类代码行数太多或者功能职责太多的时候,拆掉它。
两种拆分方法:
提取新类,当大类的部分行为可以分解为一个单独的组件,则可以使用提取类的方式拆分。
提取子类,当大类的部分行为可以以不同的方式实现或在极少数情况下使用,则可以使用提取子类方式拆分。
21. 异曲同工的类
两个类有着相同的功能,但方法名称不同。
重命名方法,并去除掉不必要的重复代码。
22. 纯数据类
纯数据类常常意味着行为被放在了错误的地方。处理数据的行为应该从客户端移至纯数据类中。
23. 被拒绝的遗赠
如果子类复用了父类的实现,就应该支持父类的接口。
24. 注释
注释是提示你,这个地方该重构啦。
如果你觉得需要写注释的时候,请先重构,试着让所有注释都变得多余。