重构——改善既有代码的设计(第2版)
Danger
所谓好,就是更少的坏味道
重构的时候不开发功能
开发功能的时候不重构
无论重构还是优化新能,都要先准备好测试代码,确保不改变代码行为
读书笔记
graph LR
subgraph 测试
T(构筑测试体系)
T->T1(撰写测试代码的最有用时机是在开始编程之前)
T->T2(只要写好一点功能,就立即添加测试并执行)
T->T3(多运用单元测试)
T->T4("花合理时间抓出大多数bug"<br>要好过<br>"穷尽一生抓出所有bug")
end
subgraph 开发
D1(定义接口、参数、返回)—>D2
D2(参数校验)—>D3
D3(数据查询)—>D4
D4(数据更新)—>D5
D5(记录日志)—>D6
D6(触发事件)—>D7
D7(单元测试)—>D8
D8(集成测试)
end
subgraph codeReview
C1(检查调用链)->C11(检查接口调用方式,get接口是否有参数过长的风险)
C1->C12(检查参数列表、参数顺序、参数类型)
C1->C13(检查判定逻辑 if empty isset ??)
C1->C14(检查程序终止条件)
C1->C15(检查临时变量名称是否一致,是否有多个用途)
C2(检查业务逻辑)
C3(检查重复逻辑)
C4(检查注释的必要性)
C5(检查换行的合理性)
end
subgraph 重构
R1(重复代码封装)
R1(注释)
end
style Sky stroke:#00F,stroke-width:12;
style Earth stroke:#030,stroke-width:12;
style F31 stroke:#333,stroke-width:4px;
style F23 stroke:#faa,stroke-width:4px;
style D1 stroke:#F00,stroke-width:4px;
style D2 stroke:#F00,stroke-width:4px;
style D8 stroke:#F00,stroke-width:6px;
style F1 stroke:#C90,stroke-width:6px;
style F2 stroke:#C90,stroke-width:6px;
style F3 stroke:#C00,stroke-width:6px;
style F4 stroke:#C00,stroke-width:4px;
style 0 fill:#2ff,fill-opacity:0.1,stroke:#F00,stroke-width:8px
style A fill:#2ff,fill-opacity:0.1,stroke:#F00,stroke-width:8px
style P3 stroke:#000,stroke-width:4px;
相关书籍
- 《解析极限编程》
- 《修改代码的艺术》
-
《数据库重构》
并行修改:
数据库重构最好是分散到多次生产发布来完成,这样即便某次修改在生产数据库上造成了问题,也比较容易回滚。 确定没有bug之后,我再删除已经没人使用的旧字段。
格言
Danger
全局数据的问题在于,从代码库的任何一个角落都可以修改它,而且没有任何机制可以探测出到底哪段代码做出了修改。
- 唯有能写出人类容易理解的代码的,才是优秀的程序员。
- 本质上说,重构就是在代码写好之后改进它的设计。
- 改进设计的一个重要方向就是消除重复代码
- 事不过三,三则重构
- 好代码的检验标准就是人们是否能轻而易举地修改它。
- 好代码应该直截了当:有人需要修改代码时,他们应能轻易找到修改点,应该能快速做出更改,而不易引入其他错误。
- 我比较喜欢让每个函数都只返回一个值,所以我会安排多个函数,用以返回多个值
- 代码被阅读和被修改的次数远远多于它被编写的次数。
- 设计模式为重构提供了目标。然而"确定目标"只是问题的一部分而已,改造程序以达到目标,是另一个难题。
- 尽管编写测试需要花费时间,但却为我节省下可观的调试时间
- 要将我的理解转化到代码里,得先将这块代码抽取成一个独立的函数,按它所干的事情给它命名
- 设计耐久性假说":通过投入精力改善内部设计,我们增加了软件的耐久性,从而可以更长时间地保持开发的快速。
- 一旦我需要思考"这段代码到底在做什么",我就会自问:能不能重构这段代码,令其一目了然?
- 重构不是与编程割裂的行为。你不会专门安排时间重构,正如你不会专门安排时间写if语句。
- 优秀的程序员知道,添加新功能最快的方法往往是先修改现有的代码,使新功能容易被加入
- 自测试代码是极限编程的另一个重要组成部分,也是持续交付的关键环节。
- 糟糕的程序结构可以慢慢理顺,把程序从一块顽石打磨成美玉。
- 哪怕你完全了解系统,也请实际度量它的性能,不要臆测。臆测会让你学到一些东西,但十有八九你是错的。
- 三大实践——自测试代码、持续集成、重构——彼此之间有着很强的协同效应。
- 如果你一视同仁地优化所有代码,90%的优化工作都是白费劲的,因为被你优化的代码大多很少被执行。
- 很多人经常不愿意给程序元素改名,觉得不值得费这个劲,但好的名字能节省未来用在猜谜上的大把时间。
- 如果你想不出一个好名字,说明背后很可能潜藏着更深的设计问题
- 据我们的经验,活得最长、最好的程序,其中的函数都比较短
- 就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数中去。
- 不必在意数据泥团只用上新对象的一部分字段,只要以新对象取代两个(或更多)字段,就值得这么做。
- 大多数编程环境都大量使用基本类型,即整数、浮点数和字符串等
- 一个体面的类型,至少能包含一致的显示逻辑,在用户界面上需要显示时可以使用
- 既然不愿意支持超类的接口,就不要虚情假意地糊弄继承体系
- 当我想好代码中应该有哪些关节时,才能使代码随着我的理解而演进。
- 如果我发现做一件事可以有更清晰的方式,我就会用比较清晰的方式取代复杂的方式。
- 模块化是优秀软件设计的核心所在,好的模块化能够让我在修改程序时只需理解程序的一小部分。
- 要维护代码库的健康发展,需要遵守几条黄金守则,其中最重要的一条当属【消除重复】
- 在我自己的编码过程中,我确实总是尽量遵循命令与查询分离的模式,因为它让我一眼就能看清代码有无副作用,而这件事情真是价值不菲。
- 如果我从不犯错,小步前进不会节省工作量;但"从不犯错"这样的梦,我很久以前就已经不做了。
- 「技术复审」是减少错误、提高开发速度的一条重要途径。
- 代码前一定要先思考,先分析逻辑,整理业务,必要时画图、流程图等,总结之后,觉得逻辑没有问题,才是下一步的写代码。
重构的难题
数据库重构
- 绝大多数的程序都和背后的数据库结构紧密的耦合在一起。
- 数据迁移
- 数据修复
- 数据初始化
接口重构
- 已经发布了的接口
- 如何用新接口替换旧接口
- 何时可以废弃旧接口
不适合重构的场景
- 代码根本无法工作或者太糟糕,重构还不如重写来的简单。
- 在项目的最后期限,应该避免重构
- 时间不充足的时候不重构
- 对代码理解不充分(所有调用处的逻辑)时不重构