什么时候应该重构?
重构应该是随时随地,没有特定时机,是为了做别的事情,重构能够帮助把事情做得更好。重构前,需要保证程序有一个可测试的机制,这些测试需要有自我检测的能力。
- 添加 feature
- 修复 bug
- 代码 Review(design or coding)
我们希望代码是:容易阅读、所有逻辑都只在唯一地点指定、新的改动不会危机现有行为、极可能简单表达条件逻辑
示例重构步骤:
- 抽离函数,减少代码长度
- 先识别局部变量和参数
- 可变变量,可以作为函数返回值
重构的过程能从局部小的重构开始,便于发现问题然后修复它。
-
兼容计算中间变量
Replace Temp with Query -
Replace Type Code with State/Strategy
State 和 Strategy 的差异主要在于我们抽象的时候,他的语义是一个 State 还是 Strategy。
可以先调整主逻辑代码,然后再调整整体的代码组织结构,代码组织结构更多的是利用设计模式 GoF 的模式。
重构意义
重构是条不归路,没有终点。
-
降低修改成本:让责任分配更加合理,代码维护更加轻松
-
改进软件设计
-
让软件更容易理解,让代码更清晰,能看到更远设计层面的未来(个人理解,在混乱中去寻找规律和抽象事情太复杂,只有在逐渐清晰的代码中去寻找)
-
更容易发现 bug
-
提高编程速度,良好的设计是软件开发速度根本
重构原则
能快乐重构的程序员特点:坚定良好代码设计是保证提高编码速度的重要因素。
什么时候不应该重构?
- 现有代码实在太乱,更适合重写
能够重构的代码是能够在大部分情况下正常运作的,如果无法运作太多问题的代码需要采用
重写
个人理解:当现有代码隐藏 bug 过多或者代码运行的一致性不够情况下,需要重新梳理逻辑进行重写会比重构更好一些,重构从日常经验来看,更多解决的是程序正确性之外的问题,可以重写后再进入重构。
个人经验:就是当在现有代码基础上难于迭代,并且容易出现质量问题情况下,就应该想到重构代码,但是重构可以有一个全局的思路,但是起点需要从局部开始进行化解,减少重构带来的影响面问题。
- 项目接近最后期限,不应该再花更多时间进行重构(个人理解这种情况下 ROI 已经不高,我们重构是为了更好的软件迭代,如果软件不再迭代就不需要重构)
重构与设计
灵活的设计方案和简单的设计方案,可能因为灵活设计方案的复杂度相比更高,软件实现后的后期维护性会比简单设计方案低,这里认为设计的可扩展性与复杂度成反比关系。
关联软件工程中铁三角关系:时间、成本、质量。使用扩展性更高的方案,成本会更高,那么就是时间和质量来 PK,在相同质量情况下(不变量),那么时间就会变长。所以这里再做一个假设,时间在 PM 倒排的情况下,往往会放弃的是质量,这里假设设计质量不变情况下,研发过程质量就会下降👇,我们推算一个未严格推导的公式如下:
假设:
- 不变量:时间 (Time)、成本 (Cost)、设计质量 (DesignQuality)
- 变量:代码质量 (CodeQuality)
- 值:可交付结果 (Deliverable)
公式:
Deliverable = Time * Cost * DesignQuality * CodeQuality
需要思考点:如果我们要将简单设计方案重构为灵活设计方案,是否可行,重构难度与代价如何?
重构与性能
短期来看,重构可能使软件变慢,但是重构的过程取得的结果,能够让软件性能的调整变得更加容易。
个人理解:主要是重构的过程会梳理代码责任分配,关注更多的软件维护性和迭代速度的指标,每个代码职责清晰,耦合和内聚的关系可能会长期 PK,耦合度很低的代码往往性能会低于更内聚的代码(无数据支持)。但是重构后,职责更加清晰,同时代码的可测试性增加,更容易识别是哪一部分的性能问题,然后针对局部进行性能度量、性能优化,最后进行测试是否影响程序正确性。
性能优化基于不要在第一次软件实现过程进行优先考虑,而是实现功能后,针对需要关注性能的局部进行改进,通常已经到了项目中后期阶段。
这里作者还有一个有意思的故事,主要结论是:我们针对性能优化的时候,不能完全靠猜测来指导我们的方向,可能会花费很长的时间去解决问题。能够使用真实度量的方式,来一步步去解决度量出来的实际问题,直到解决客户可接受的性能指标。