开始
本章重构:
- 将原函数分解成一组嵌套的函数
- 分离计算逻辑与输出格式化逻辑
- 为计算器引入多态性来处理计算逻辑
好的代码的检验标准就是人们是否能构轻易地修改它注: 小步累积
重构的原则
何谓重构
- 名: 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本.
- 动: 使用一系列重构手法,在不改变软件可观察行为的前提下,高速其结构.
重构时不添加新功能,添加新功能时不重构.
为何重构
改进软件的设计
使软件更容易理解
帮助找到BUG
提高编程速度
何时重构
事不过三, 三则重构
- 预备性重构: 在添加新功能之前进行重构
- 帮助理解的重构:
- 捡垃圾式重构: 不想从眼下正要完成的代码上跑题太多,但也不想把垃圾留在原地.所以在重构可以很快完成时就立即重构,如果需要花一些精力,可以做一个TODO.至少要让营地比你到达时干净.
- 有计划的重构和见机行事的重构每次修改时,首先令修改很容易,然后再进行这次容易的修改.
- 长期重构如果想替换掉一个正在使用的库,需要很长时间,可以先引入一层新的抽象,使其兼容新旧的两个库的接口.一旦调用方已经完全为使用这层抽象,替换掉下面的库就会容易很多.
- 重审代码时重构
- 何是不应该重构
- 如果我看见一块凌乱的代码,但并不需要修改它,那么我就不需要重构它.
- 如果丑陋的代码能被隐藏在一个API之下,我就可以容忍它继续保持丑陋.
- 如果重写比重构还容易,就别重构了.
重构的挑战
- 延缓新功能开发
代码的坏味道
神秘命名
重复代码
过长函数
现代编程语言几乎已经完全免除了进程内的函数调用开销.
过长的参数列表
- 如果可以向某个参数发起查询而获得另一个参数的值,那么就可以使用以查询取代参数去掉这第二个参数.
- 如果正在从现有的数据结构中抽出很多数据荐,就可以考虑使用保持对象完整手法
- 如果有几项参数总是同时出现,可以引入参数对象
- 如果某个参数被用作区分函数行为的标记,可以使用移除标记参数.
全局数据
可以把全局数据用一个函数包装起来,至少能看见修改它的地方.
可变数据
发散式变化
每次只关心一个上下文
霰弹式修改
依恋情结
总是将一起变化的东西放在一块儿.
数据泥团
基本类型偏执
可以运用使用对象取代基本类型
重复的switch
任何switch语句都应该用以多态取代条件表达式.
循环语句
可以使用管道取代循环
冗赘的元素
有些类或者额外的结构在设计过程中不再有用,就把它抛弃
夸夸其谈通用性
那些我们想的总有一天需要做这些事,并且企图以各式各样的钩子和特殊情况来处理一些非必要的事情.
临时字段
过长的消息链
可以使用隐藏委托关系
中间人
如果你看到某个类中有一半的函数都委托给其它类,这样就是过度运用.应该移除中间人.
内幕交易
如果两个模块有共同的兴趣,可以尝试再新建一个模块,把这些共用的数据放在一个管理良好的地方
过大的类
如果想利用单个类做太多的事情,其内往往就会出现太多字段
异曲同工的类
纯数据的类
被拒绝的遗赠
注释
如果需要太多注释注解的代码,往往意味着需要重构
构筑测试体系
自测试代码的价值
介绍重构名录
第一组重组
提炼函数
将意图与实现分开
内联函数
提练变量
内联变量
改变函数声明
- 函数改名
- 添加参数
- 移除参数
- 修改签名
封装变量
封装能提供一个清晰的观测点,可以由此监控数据的变化和使用情况。
变量改名
引入参数对象
将多个总是同时出现的值参数组合成类,形成一个概念,可以更好的理解业务。
函数组合成类
函数组合成变换?
拆分阶段
每当看见一段代码在同时处理两件不同的事,就可以把它拆分成各自独立的模块。
封装
封装记录
以数据类取代记录
封装集合
不要直接使用集合的字段,而是通过定义类上的方法来代替。
以对象取代基本类型
一旦发现对某个数据的操作不仅仅局限于打印时,就为它创建一个类。
以查询取代临时变量
把临时变量抽取成函数
提炼类
内联类
隐藏委托关系
移除中间人
替换算法
搬移特性
在不同的上下文之间搬移元素
搬移函数
搬移函数的一个直接原因是,它频繁引用其他上下文中的元素,而对自身上下文中的元素却关心甚少. 为了做出决定,我们需要仔细检查函数当前上下文与目标上下文之间的区别,需要查看函数的调用都有谁,它自身又调用的哪些函数.
搬移字段
可能是因为发现每当调用某个函数时,除了传入一个记录参数,还总是需要同时传入另一条记录的某个字段一起做为参数.
搬移语句到函数
如果发现调用某个函数时,总有一些相同的代码也需要每次执行,那么就需要考虑将此段代码合并到函数里头.这样, 日后对这段代码的修改只需要修改一处地方,还能对所有调用者同时生效. 如果某些语句与一个函数放在一起更像一个整体,并且更有助于理解,那么就会将语句搬移到函数里去.
搬移语句到调用者
以函数调用取代内联代码
抽取函数
移动语句
让存在关联的语句一起出现,可以全代码更容易理解要判断一次语句移动是否安全,都意味着我得真正理解代码的工作原理,以及运算之间的组合方式等.
拆分循环
让每个循环只做一件事儿,便于理解代码.
以管道取代循环
移除死代码
版本控制系统出现之前注释还是有必要的,版本控制系统出现之后就可以直接删除无用代码了.
重新组织数据
拆分变量
让每个变量只承担一个责任
字段改名
以查询取代派生变量
将引用对象改为值对象
将值对象改为引用对象
简化条件逻辑
分解条件表达式
合并条件表达式
以卫语句取代嵌套条件表达式
卫语句告诉阅读者:这种情况不是本函数的核心逻辑所关心的,如果它真发生了,请做一些必要的整理工作,然后退出.
以多态取代条件表达式
引入特例
引入断言
如果发现代码假设某个条件始终为真,就加入一个断言明确说明这种情况