重构
概念与原则
定义
在不改变代码外在行为的前提下, 对代码做出修改, 以改进程序的内部结构。
何时重构
- 三次法则:事不过三,三则重构
- 新增功能:让未来增加特性更快速、更流畅
- 修复缺陷:因代码不够清晰而无法一目了然发现错误
- 代码评审:经验传递,改善设计
何时不重构
- 代码太混乱,设计完全错误
- 时间不够:明天就是deadline
- 工作量过大:推迟重构
- 没有好的思路
原则和流程
一次只做一件事:添加新功能时不应该修改既有代码;重构时不能再添加功能。
测试保护--》识别味道--》采用手法--》小步前进
每一步都需要测试。
代码怪味道
代码风格
风格一致、容易阅读。
命名、排版、函数、变量等。
冗余和重复
重复是万恶之源。拷贝一时爽,维护两行泪。
过多的注释
可能意味着本身代码就很糟糕。
注释
应该解释why,而不是how和what,代码本身告诉你how。
过度设计
过度关注未来可能的变化,增加了不必要的东西。
局部膨胀
过长参数列表
参数之间约束关系不明,难以调用;参数分类,结构体。
构造函数大量参数
参数对象、生成器模式。
耦合结构不良
发散式变化
某个模块(类、函数)经常因为不同的原因在不同的方向上发生变化。
解决方法:按不同变化方向拆分。
散弹式修改
遇到变化时必须在许多不同的模块(类、函数)内做出许多小的修改。
重复的switch
解决方法:多态/表驱动/策略模式。
临时字段
对象的某个变量仅为特定场景而设,或者只在该对象某一段生成周期内生效,在其他场景或周期调用时产生错误。
解决方法:将临时字段作为参数或者封装成子类。
工程工具
converity/fortify/codemars /binscope
重构手法
- 抽:抽取方法
- 替:内联方法
- 组:抽取类
- 改:重命名
- 移:移动方法
简化语句
移动语句
让存在关联的东西一起出现
合并条件表达式
以卫语句取代嵌套条件表达式
选中最外层需要被替换的条件逻辑,将其替换为卫语句。
重组函数
提炼函数
将部分代码提炼成函数,通过函数命令标识函数功能,实现自注释。
起名字是最重要的,如果名字不好取,说明提炼可能不合理。
内联函数
消除间接性,实现更内聚,方便后续重构。
将查询函数和修改函数分离
任何查询类的函数,都不应该有看得到的副作用。
移除标记参数
根据标记参数提供不同函数给调用者调用。
重组数据
拆分变量(分解临时变量)
变量被多次就赋值,且赋值的意义不同,意味着他们在函数中承担了多个职责。
提炼类
将不同职责的代码提取到独立的类(模块)。
内联类
消除多余的间接性,重新组织类,以便按照其他方向进一步拆分。
类继承体系重构
函数上移/下移
字段上移/下移
系统级重构
重构级别
L0:功能正确
L1:无漏洞风险
L2:代码整洁
L3:架构敏捷
L4:持续演进