重构 01:前言
Translated from:Refactoring
重构是一个系统化的过程,在不创建新功能的情况下改进代码,可以将一团糟的代码转化为干净的代码和简单的设计。
重构工作包含:简化代码
重构的主要目的是对抗技术债务。它将一团乱麻转化为干净的代码和简单的设计。
很好!但什么是干净的代码呢?下面是它的一些特点。
1. 干净的代码对于其他程序员来说是显而易见的。
这里说的不是超级复杂的算法。糟糕的变量命名,臃肿的类和方法,神奇的数字–你能想到的–所有这些都会让代码变得混乱和难以控制。
2. 干净的代码不包含重复的内容。
每次要对重复的代码进行修改时,都要记得对每个相同的部分进行同样的修改。这增加了记忆负担,同时也减缓了开发进度。(译者:代码质量也很堪忧)
3. 干净的代码包含了最少数量的类和其他可移植的组件。
代码少了,脑子里要装的东西就少了。更少的代码就是更少的维护。代码少就是bug少。代码就是负担,要短小精悍。
4. 干净的代码通过所有测试。
当你的测试只有95%通过时,你就知道你的代码很脏。当你的测试覆盖率为0%的时候,你就知道你完蛋了。
5. 干净的代码更容易维护,成本更低!
重构工作包含:解决技术性债务
每个人都会尽自己最大的努力,从零开始写出优秀的代码。可能没有一个程序员会故意写出不干净的代码来损害项目。但是,干净的代码在什么时候会变得不干净呢?
关于不干净代码的 “技术债务 “的比喻最初是由 Ward Cunningham 提出的。
如果你从银行获得贷款,这可以让你更愉快的进行购物。当然,你要付出额外的代价–不仅要偿还本金,还要偿还贷款的额外利息。最终,你甚至可以积累茫茫多的利息,以至于利息金额超过了你的总收入,让你无法全额还款。
同样的事情也会发生在代码上。你可以暂时不为新功能写测试而加快进度,但这将使你每天的进度逐渐变慢,直到你最终通过写测试来还清债务。
技术性债务的原因
1. 业务压力
有时,业务方可能会迫使你在功能完全完成之前就将其发布。这种情况下,为了掩盖未完成的功能,代码中会充斥着补丁和不成熟的代码。
2. 对技术性债务的后果缺乏了解。
有时,你的老板可能不理解技术债务有 “利息”,因为随着债务的积累,它会减缓开发的速度。这可能会使团队很难将时间用于重构,因为管理层没有看到它的价值。
3. 组件严重耦合。
当项目类似于整体而不是单个模块的产品时。 在这种情况下,对项目某一部分的任何更改都会影响其他部分。 团队开发变得更加困难,因为开发工作都将耦合在一起,可能都无法进行独立的开发。
4. 缺乏测试。
缺乏及时测试的代码虽然速度快,但是对项目充满了风险和问题。在最糟糕的情况下,这些改动在没有任何事先测试的情况下就被部署和运行到生产中。其后果可能是灾难性的。例如,一个看似无害的热修复程序可能会向成千上万的客户发送一封奇怪的测试电子邮件。甚至更坏的情况,缺陷会重置或破坏整个数据库。
5. 缺乏文档。
这就减缓了项目引进新人的速度,如果关键开发人员离开,就会使项目发展停滞不前。
6. 团队成员之间缺乏互动。
如果知识库没有在整个公司广泛运用,人民最终会在对项目的流程和信息的理解上因信息不同步导致问题发生。如果初级开发人员没有得到导师的正确培训,这种情况会更加严重。
7. 在长分支上持续开发。
这可能会导致技术债务的积累,当分支合并时,技术债务就会增加。在一根分支上单独提交的代码越多,合并时产生的技术债务总额就越大。
8. 不断推迟重构的时间。
项目的需求是不断变化的,在某些时候,可能会发现部分代码已经过时,已经变得很繁琐,必须重新设计以满足新的需求。
另一方面,项目的程序员每天都在过时的部分中不断编写新的代码。因此,重构的时间拖得越长,将来需要重构的代码依赖就越多。(译者注:大概是指数级吧)
9. 没有遵循的编码规范。
当每个在项目中工作的人都按照自己认为合适的方式写代码时,就会出现这种情况。
10. 能力问题。
参与的开发 too simple too naive,完全不知道怎么写代码。
重构的时机
“三次”定律…
- 当你写第一次代码时,直接写就是了。
- 当你写第二遍,发现代码有点相似,仍然可以直接写。
- 同样的逻辑写第三次是,可以开始重构了!
当添加功能时…
- 重构可以帮助你理解别人的代码。如果你不得不处理别人的脏代码,试着先重构一下。干净的代码更清晰易懂,同时,赠人玫瑰,手有余香。
- 重构让你更容易添加新功能。在干净的代码中进行修改要容易得多。
当修复缺陷时…
代码中的bug就像现实生活中的蟑螂一样:它们生活在代码中最黑暗、最肮脏的地方。清理你的代码,bug就会自己跑出来。
老板们很欣赏承担主动重构任务的开发人员,因为它省去了以后专门安排人力的重构任务。Happy bosses make happy programmers!
当代码审查时…
代码审查可能是发布之前整理代码的最后机会。最好与原作者结伴进行这种审查。这样你可以快速修复简单的问题,并评估修复较难问题的时间。
如何进行重构
重构应该小步快进,每一个改动都会让现有的代码变得稍微好一点,同时还能让程序保持工作状态。
用正确的方式进行重构的检查表
√ 代码应当越来越简洁
如果重构后代码仍然不干净… 抱歉,但你刚刚可能浪费了一个小时的时间。试着找出为什么会发生这种情况。
当你合并一堆重构导致的小修改点时,经常会发生这种情况。当你开发压力非常大时,会感到抓狂。
但在处理极度草率的代码时,也会出现这种情况。无论你如何改进,代码整体上仍然是一场灾难。(译者注:屎山无论怎么整理终究还是屎山。)
这是你应该考虑彻底重写这部分代码。但在这之前,你应该已经写好了测试,并预留一大块开发时间。否则,你还是会回到第一段的情形。
√ 不应该在重构过程中添加新功能
不要把重构和新功能的直接开发混合在一起。将这些过程分开,不要同时在一个commit里面既包含重构的代码又包含新功能。
√ 重构后所有现有的测试都必须通过。
有两种情况下,重构后的测试用例会崩坏:
- 你在重构过程中犯了错误。 这个就不多说了:继续修正错误。
- 你的测试用例太差了。 例如,你在测试类的私有方法。在这种情况下,测试是罪魁祸首。你可以重构测试本身,或者编写一套全新的更好的测试。避免这种情况的一个好办法就是编写行为驱动开发BDD式的测试。