一言难尽的重构学习笔记
1. 什么是重构
具体来说就是在不改变代码功能行为的情况下,对其内部结构的一种调整。需要注意的是,重构不是代码优化:
- 重构:注重的是提高代码的可理解性与可扩展性,对性能的影响可好可坏
- 性能优化:让程序运行的更快,但最终的代码可能更难理解和维护
2. 为什么要重构
改善程序的内部设计
如果没有重构,在软件不停的版本迭代中,代码的设计只会越来越腐败,导致软件开发寸步难行。人们只为了短期目的而修改代码时,往往没有完全理解整体的架构设计(在大项目中常有这种情况,比如在不同的地方,使用完全相同的语句做着同样的事情),代码就会失去自己的结构。代码结构的流失具有累积效应,越难看出代码所代表的设计意图,就越难保护其设计。
我们几乎不可能预先做出完美的设计,以面对后续未知的功能开发,只有在实践中才能找到真理。
使得代码更容易理解
在开发中,我们需要先理解代码在做什么,才能着手修改,很多时候自己写的代码都会忘记其实现,更不论别人的代码。可能在这段代码中有一段糟糕的条件判断逻辑,又或者其变量命名实在糟糕又缺乏注释,需要花上好一段时间才能明白其真正用意。
提高开发速度 && 方便定位错误
提高开发的速度可能有点"反直觉",因为重构在很多时候看来是额外的工作量,并没有新的功能和特性产出,但是减少代码的书写量(复用模块),方便定位错误(代码结构优良),这些能让我们在开发的时候节省大量的时间,在后续的开发中"轻装上阵"。
3. 何时重构
重构不是一件应该特别拨出时间做的事情,重构应该随时随地进行。不应该为重构而重构,之所以重构,是因为我们想做别的什么事,而重构可以帮助我们把那些事做好。
三次法则:事不过三,三则重构。
- 添加功能时重构
- 修补错误时重构
- 复审代码时重构
4. 识别代码的臭味道
| 臭味道 | 英文 | 说明 |
|---|---|---|
| 重复代码 | Duplicated Code | 同一段代码在多处出现 |
| 过长函数 | Long Method | 函数太长,难以理解 |
| 过大的类 | Large Class | 类承担了太多职责 |
| 过长参数列 | Long Parameter List | 参数太多,难以调用 |
| 发散式变化 | Divergent Change | 一个类受多种变化的影响 |
| 霰弹式修改 | Shotgun Surgery | 一种变化引发多个类相应修改 |
| 依恋情结 | Feature Envy | 函数对某个类的兴趣高过自己所处类的兴趣 |
| 数据泥团 | Data Clumps | 相同的若干项数据出现在不同地方,应该有自己的对象 |
| 基本类型偏执 | Private Obsession | 很多人不愿意在小任务上运用小对象 |
| switch 惊悚现身 | Switch Statements | switch 语句在多处重复,一改则需全改 |
| 平行继承体系 | Parallel Inheritance Hierarchies | 为某个类增加子类时,必须为另一个类也增加子类 |
| 冗赘类 | Lazy Class | 如果一个类不值得存在,那就让它消失 |
| 夸夸其谈的未来星 | Speculative Generality | 预留的无用的抽象类,无用的抽象参数 |
| 令人迷惑的暂时字段 | Temporary Field | 类中某个字段只为某些特殊情况而设置 |
| 过度耦合的消息链 | Message Chains | 用户向一个对象请求另一个对象,再向后者请求另一个对象…… |
| 中间人 | Middle Man | 无用的委托,过多的中间层 |
| 狎昵关系 | Inappropriate Intimacy | 两个类过于亲密,一个类过于关注另一个类的成员 |
| 异曲同工的类 | Alternative Classes with Different Interfaces | 不同名字的类或函数,做着相同的事 |
| 不完美的库类 | Incomplete Library Class | 类库设计不可能完美 |
| 纯数据类 | Data Class | 拥有一些字段和访问函数,除此之外一无长物 |
| 被拒绝的遗赠 | Refused Bequest | 子类不想继承超类所有的函数和数据,只想挑几样 |
| 过多的注释 | Comments | 代码本身不够清晰才需要大量注释 |
