重构的关键是:运用大量微小的、保持软件行为的步骤,一步步达成大规模的修改。
重构可能会带来什么?
- 改变代码调用栈,程序性能有所比变化
- 改变接口声明,改变模块接口
- ...
重构和性能优化的区别:
- 都改变了代码
- 重构是为了代码更好理解
- 性能优化是为了代码运行更快,但最终代码可能更难维护。
开发时的两顶帽子🎩(两个阶段,反复横跳)
- 添加新功能时,只关注添加新功能,不修改既有代码。添加测试并让测试完成。
- 重构时,只关注调整代码结构,(非不要时)不添加和修改测试。
设计耐久性假说
何时重构
事不过三,三则重构
预备性重构:重构最好的时机是添加新功能之前
- 对于某个函数提供了我需要的大部分功能,只需要修改几个地方即可重复使用,此时应该复制粘贴,而是进行函数参数化,进行重构。
- 重构之后的代码出现相同bug的概率会很低,如果通过负责粘贴,我们可能还需要反复修复同样的bug。
捡垃圾式重构
- 对于简单的重构,可以立即去执行
- 对于比较复杂的重构,需要记下来,后续再进行重构
重构时的权衡
写代码时,会做出很多权衡取舍
- 参数化需要做到什么程度?
- 函数之间的边界应该划在哪里?
- 对于昨天的功能完全合理的权衡,在今天要添加新功能时可能就不再合理。
需要不断的权衡现实情况的变化,简洁的代码重构起来会更加容易。
添加新功能最快的方法往往是先修改现有的代码,使新功能更容易被加入。
何时不重构
- 在不理解其工作原理时,不重构,因为此时重构没有价值
- 当重写比重构更加容易时,进行重写,此需要具备一定的判断。
重构代码的目的是,添加新功能更快,修bug更快,程序代码更好理解。重构总是由经济利益驱动的。意义不在与把代码库打磨的光鲜亮丽(误区)。
对重构采取不同的策略
- 简单设计,增量式设计。
- 思考“以后重构有多困难”,判断此时是否需要进行重构,添加代码灵活性。
重构与性能的权衡
- 重构可能使软件运行更慢,但它也使软件的性能优化更容易。
- (除了对性能严格要求的实时系统),其他情况下,编写快速软件的秘密就是:先写出课调优的软件,再调优到足够的速度。
- 有时候,我们大部分时间都花在了优化一小部分代码上,但往往这部分代码大多很少被执行,可能花费的功夫是白费的。
- 需要对程度有清晰的认识,使用工具测试程序的真实情况,找到问题的真实原因。
重构场景下的代码方案
重构代码导致的分支问题
- 合并和集成是两回事:
- 如果从主线合并到某个分支,只是单向的代码移动,主线没有改变。
- 如果是集成,则是双向的。
- 假设我重构了代码,修改了源函数名称,a此时提交了代码到主线,而我使用集成,把主线的代码进行集成,此时代码可能就会出现问题,因为a的代码可能存在旧函数的引用,而此时我的重构代码也集成到了主线中,导致代码被破坏。
引入测试
- 想要重构之前,需要有可以自测的代码。
- 消除“重构风险太大,可能引入bug”的担忧。
- 一开始就写能自测试的代码
对数据库的重构
- 最好是分散到多次生产发布来完成。这样子如果造成问题,比较容易回滚。
- 运用“并行修改”:
- 比如要改名一个字段。
- 在第一次提交会新增一个字段,但暂时不使用它。之后修改数据写入的逻辑,使其同时写入新旧两个字段,随后修改读取数据的地方,逐个修改为新字段。
- 暂停一下,看看有没有bug冒出来,确定没有bug后,再删除旧字段。
程序优化的流程
- 先用工具监控程序的运行,找到消耗时间和空间的地方。
- 小幅度进行修改,每一步都需要编译、测试,如果没有提高性能,则撤销修改,重复执行这个流程,直到获得满意的性能为止。
- 前期编写好的代码,可以让我们带入到范围较小的代码快,性能的调整也会更容易些;代码清晰,可以更好的理解自己的选择,清楚哪种调整起关键的作用。