前段时间读过martin fowler的《重构 ,改善既有代码的设计 》,不禁感叹:他老人家绝对对得起大师这个名号啊!对于入行不久的程序员来说,读这本书对提高代码质量肯定有帮助。就重构这个话题,还和部门同事做了次交流,将交流时的文档整理在此仅作备忘。
软件的成本
N 年前, Yourdon 和 Constantine 在 Structured Design 一书中将经济学作为了软件设计的底层驱动力,软件设计应该致力于减少整体成本。
COST total = COST develop + COST maintain
COST maintain = COST understand + COST change + COST test + COST deploy
软件的维护成本占了软件总成本的大部分,想要节约软件成本就要从软件的维护成本下手,而维护成本中理解老代码和修改老代码又占了绝大部分。可见,提高代码可理解性是多么重要,这时重构闪亮登场,为我们改善代码可读性提供了指导性的一些原则和手法。
什么是重构
看看 Martin Fowler的定义:
在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。重构是一种有纪律的、经过训练的、有条不紊的程序整理方法,可以将整理过程中不小心引入错误的机率降到最低。本质上说,重构就是在代码写好之后改进它的设计。
--Martin Fowler
重构 【 名词 】 :对软件内部结构的一种调整,目的是在不改变软件之可察行为 前提下,提高其可理解性,降低其修改成本 。
重构 【 动词 】 :使用一系列重构准则(手法),在不改变软件之可察行为 前提下,调整其结构。
关键的一点是重构离不开单元测试,测试驱动开发的模式。在已有单元测试的基础上进行代码重构,小步前进。
另外一个比较好的建议是 Kent Beck所说的 两顶帽子 :使用重构技术开发软件时,你把自己的时间分配给两种截然不同的行为 : 「添加新功能」和「重构」。
重构的好处
- 使软件更容易理解
- 帮你找到 bug
- 提高编程速度
重构时机
- 三次法则
- 添加新功能时
- Bugfix 时
- Code review 时
重构与设计
重构与设计并不是矛盾的,而是互为补充的:
- 所有的设计都不可能做到滴水不漏,这个时候就需要重构来堵设计的漏洞;
- 不能依赖重构而不重视重构。
重构与性能
重构和性能优化是不同维度的手法,重构的目的是提示程序的可理解性和可维护性,而优化则是侧重提升性能。重构为了提高代码可读性往往还会导致性能不是最优,考虑“二八原则”,如果这段代码不会显著导致程序性能严重下降,我们选择重构。
坏味道及应对重构手法
• 重复的代码
– 提炼函数,提炼类,上移函数,塑造模板函数
• 过长函数
– 提炼函数,查询取代临时变量,函数对象取代函数,分解条件表达式
• 过大类
– 提炼类,提炼子类,提炼接口,以对象取代数据值
• 过长参数列
– 函数取代参数,引入参数对象,保持对象完整
• 发散式变化
– 提炼类
• 散弹式修改
– 搬移函数,搬移值域,将类内联
• 依恋情结
– 搬移函数,搬移值域,提炼函数
• 数据泥团
– 提炼类,引入参数对象,保持对象完整
• 基本型别偏执
– 以对象取代数据值,提炼类,引入参数对象,以对象取代数组,以类取代型别码,以子类取代型别码,以 State/Strategy 取代型别码
• Switch 惊悚现身
– 多态取代嵌套条件表达式,以 State/Strategy 取代型别码,以明确函数取代参数
• 平行继承体系
– 搬移函数,搬移值域
• 冗余类
– 内联类,折叠继承体系
• 夸夸其谈未来性
– 折叠继承,将类内联,移除参数,重命名函数
• 令人迷惑的暂时值域
– 提炼类
• 过度耦合的消息链
– 隐藏委托
• 中间转手人
– 消除中间人,将函数内联,以委托取代继承
• 狎昵关系
– 搬移函数,搬移值域,改双向关联为单向,以继承取代委托,隐藏委托
• 异曲同工的类
– 重命名函数,搬移函数
• 不完美的类库
– 引入外加函数,引入本地扩展
• 单纯数据类
– 搬移函数,封装值域,封装集合
• 被拒绝的遗赠
– 以委托取代继承
• 过多的注释
– 提炼函数
重构法则分类
重新组织函数
- 提炼函数
- 将函数内联化
- 将临时变量内联化
- 以查询取代临时变量
- 引入解释性变量
- 剖解临时变量
- 移除对参数的赋值操作
- 以函数对象取代函数
- 替换算法
在对象间搬移特性
- 搬移函数
- 搬移值域
- 提炼类
- 将类内联化
- 隐藏委托关系
- 移除中间人
- 引入外加函数
- 引入本地扩展
重新组织数据
- 自封装值域
- 以对象取代数据值
- 将实值对象改为引用对象
- 将引用对象改为实值对象
- 以对象取代数组
- 将单向关联改为双向
- 将双向关联改为单向
- 以符号常量或字面常量取代魔法数
- 封装值域
- 封装集合
- 以数据类取代记录
- 以类取代型别码
- 以子类取代型别码
- 以 State/Strategy 取代型别码
- 以值域取代子类
简化条件表达式
- 分解条件表达式
- 合并条件表达式
- 合并重复的条件判断
- 移除控制标记
- 以卫语句取代嵌套条件式
- 以多态取代条件式
简化函数调用
- 重新命名
- 重新命名函数
- 查询和修改分离
- 以明确函数取代参数
- 保持对象完整
- 以函数取代参数
- 引入参数对象
- 以工厂函数取代构造函数
- 以异常取代错误码
-
将查询函数和修改函数分离
处理概括关系
- 值域上移
- 函数上移
- 函数下移
- 值域下移
- 构造函数本体上移
- 提炼子类
- 提炼超类
- 折叠继承体系
- 塑造模板函数
- 以委托取代继承
- 以继承取代委托
最后,附上ppt。
ps.
je的编辑器真够难用的啊:(