重构指什么
这里的 重构行为 是指在不改变软件功能的前提下,通过一些 重构手段 调整代码结构使其更易维护、代码更加健壮,并且能有效降低项目的学习成本。
为何重构
- 改进软件设计
- 使软件更易读
- 帮助解决一些诡异的BUG
- 提高变成速度
重构的时机很重要
我们何时重构呢?一般来说,我们很难说找到一个比较长的周期什么事都不做,只重构。那么重构应该就穿插在我们日常工作中。
- 预备性重构:添加新的功能之前做技术设计,发现这部分代码设计的不够合理我们可以在这时候进行该功能的重构
- 帮助理解的重构:当我读不懂这段代码的时候,我就要考虑进行重构,或者说的极限点,当我觉得这部分代码需要些注释才能解释明白的时候我可以考虑进行重构
- 捡垃圾式的重构:在读代码时候发现这段代码逻辑相同,或者结构迂回有问题
- 有计划的重构和见机行事的重构:在写新功能时候顺手就把不合理的代码改掉
- 长期重构:一些比较大的架构需要调整的视乎
review时候重构
另外重构尽量在程序员之间内部闭环少让产品、项目经理参与他们可能出于成本考虑阻碍重构,另外重新比重构划算时候就重写吧
重构的挑战是什么
- 延缓新功能开发:如果一块代码被封装隔离起来了,即便写的很糟只要你不用理解它你就可以不重构它
- 代码所有权:一旦接口发布出去了就不好在重构了,否则需要做大量兼容工作
- 分支:主干发布,分支开发这种会导致,merge地狱出现
- 测试:重构要提前想好测试方案,减少出现BUG的几率
- 遗留代码:祖传代码没人敢动,还是提前准备充足测试方案
- 数据库的重构:做好版本管理,将改动合并到代码库中便于回滚
代码有哪些坏味道
在我们工程的代码中出现如下的情况,会使我们的代码产生坏的味道入股不管,这些坏味道很快就会让我们的工程腐败,所以我们可以考虑对这些地方进行进行重构。
神秘命名
当我们发现我们对方法或者函数命名不够准确,很难理解这时候我们应该考虑架构是否合理,代码结构是否应该调整。
解决方法:
- 改变函数声明
- 字段改名
- 变量改名
重复代码
在一个地方看到相同的代码结构,导致后期维护需要维护多个副本
解决方法:
- 提炼函数
- 如果只是相似而不是相同,我们可以先 移动语句 重组代码顺序
- 如果重复代码在不同的子类中,我们可以用 函数上移 将代码放到父类中
过长函数
过长的函数会导致代码很难理解
解决方法:
- 提炼函数,拆分成多个小函数
- 以查询取代临时变量,减少难以理解的临时变量
- 引入参数对象、保持参数完整性,减少过长的参数列表
- 如果还有_太多参数和临时变量,我们可以以 以命令取代函数 将这个长的函数封装成一个命令类,构造函数是参数
- 对于循环 拆分循环 ,对于条件表达式可以 分解条件表达式,或者以 多态处理
过长参数列表
危害同上
解决方法:
- 假设参数列表中的一个参数,是通过另一个参数求解出来的,我们可以用 已查询取代参数 来去掉这个参数
- 保持参数完整性
- 已入参数对象
- 移除标记参数
- 如果多个函数引用相同的参数列表,可以 函数组合成类
全局数据
如果没有对数据的修改封装,很可能会造成系统BUG
解决方案
- 少用,如果必须要用缩短其作用域(包级别调用)
- 封装变量
可变数据
对数据的修改很可能会导致BUG,同上
解决方案
- 封装变量
- 拆分变量
- 移动语句,提炼函数
- 将查询函数和修改函数分离
- 移除设值函数
- 如果可变数据能在其他地方计算出来,我们可以 查询取代派生变量 即:字段只没有变,每次返回的都是计算过程
- 函数组合成类、函数组合成变换、引用对象改为值对象
发散式变化
某个模块因为不同的原因在不同的地方发生变化,最终导致这个模块无法维护,这个其实违反了地耦合的原则,一个类应该指关心自己的上下文。
解决方案
- 先用拆分阶段将两者拆分、然后用搬移函数将处理逻辑分开。
- 如果函数内部混合了俩类处理逻辑、先提炼函数讲起拆分,如果是以类的方式定义的可以用提炼类来做拆分
霰弹式修改
如果每次修改一个地方,都需要在很多其他类中做修改,这个其实违反了高内聚的原则。
解决方案
- 搬移函数或者搬移字段把要修改的代码放在同一个模块
- 如果有很多函数都在操作想死的数据,可以使用 函数组合
- 如果优先函数是功能转换或者数据填充,可以使用 函数组合成变换
- 如果一些函数输出可以组合后提供给一段专门使用这些计算结果的逻辑,可以使用 拆分阶段
- 一个常用策略是 内联函数 或者是 内连类 将不该分散的逻辑拽到一起。
依恋情节
todo