书籍导读 《重构——改善既有代码的设计》
任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员
–Martin Fowler
1. What ? 什么是重构?
重构,这是大家在日常工作中很常用的一个词,但严谨的来说,很多时候,我们的所说的重构不是重构,而是重写!
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构!
重构过程不能破坏甚至改变软件外在功能,如果进行一次重构重构前,这段代码有一个bug, 那么重构后这个bug应该还在!
如何进行“不改变软件可观察行为”的修改动作,比如:
删除无人使用的变量、函数或类。
添加无人使用的变量、函数或类。
将对一个变量或函数的引用,改为引用另一个与之完全相同的变量或函数。
从这样简单得近乎“废话”的基础开始,构建起了整个重构的理论体系
阅读第一章
2. Why? 为什么重构?
软件的设计不必甚至不可能是一开始就做好的,软件一定会不断修改。所以真正重要的不是如何提前做一个完美的设计,而是在不断修改的过程中,让软件的设计水平不变差,乃至变得更好。
在代码写好后改进它的设计,设计不是在一开始完成的,而是在整个开发过程中逐渐浮现。重构的每个步骤都很简单,甚至时过于简单,但这些小小的修改累积起来就可以根本改变设计质量!
改进软件的设计
提高代码质量,更易被理解
尽早的发现bugs
添加功能更快!修复bug更快!
项目的维护成本远高于开发成本
3. When ? 什么时候重构?
在编程的每一分钟里,随时进行重构
三次法则:事不过三,三则重构!
第一次做某事时,放手去做,第二次做类似的事会产生反感,但还是可以去做,第三次再做类似的事,你就应该重构!
见机行事:
最佳时机:添加新功能前, 尤其是当你想要复制粘贴时
修改BUG时,
捡垃圾式重构,重构需要花费一定的精力时,至少让营地比你到达时更干净!
重构不是与编程割裂的行为,你不用专门安排时间重构,在你做其他事时自然发生!
Code Review 时:
* 每一次做代码评审的时候
什么时候不应该重构:
不需要修改时,比如丑陋的代码被隐藏在一个API下
- 重写比重构更容易
4. Where? 重构哪里?
阅读第三章:代码的坏味道
- 神秘命名
- 重复代码
- 过长函数
- 过长参数列表
- 全局数据
- 可变数据
- 发散式变化
- 霰弹式修改
- 依恋情节
- 数据泥团
- 基本类型偏执
- 重复的switch
- 循环语句
- 冗赘的元素
- 夸夸其谈的通用性
- 临时字段
- 过长的消息链
- 中间人
- 内幕交易
- 过大的类
- 异曲同工的类
- 纯数据类
- 被拒绝的遗赠
- 注释
过长函数
1 | 没有什么是增加一层“间接层”不能解决的。如果有,那就增加两层 |
1 | 小函数会给代码的阅读者带来一定的负担,因为必须不断切换上下文,才能看明白函数在做什么 |
1 | 当你感觉需要以**注释**说明点什么的时候 ,我们就需要把说明的东西写入一个独立函数中去 ,并以其用途(而非实现手法)命名 ,我们可以以一组甚至短短一行代码来做这件事 ,哪怕替换后的函数调用比函数自身还长,只要函数名可以解释其用途,就值得 ,关键不在与函数的长度,而是函数“做什么”与“如何做”之间的语义距离 |
1 | 99%的场合,把函数变短,使用**提炼函数**方法就足够了如果有大量参数和临时变量,可以使用**已查询代替临时变量**消除临时变量**引入参数对象** 和 **保持对象完整** 可以将过长的参数列表变的简洁... ... |
5. Who ? 谁来重构?
代码属于集体
集体对代码负责
每个人都有义务消除坏味道
6. How ? 如果重构
阅读重构名录,《重构》时一本指导手册,参照这本手册,逐个。
第一组重构
提炼函数
内联函数
提炼变量
内联变量
改变函数声明
封装变量
变量改名
引入参数对象
函数组合成类
函数组合变换
拆分阶段
封装
搬移特性
重新组装数据
简化条件逻辑
重构API
处理继承关系
8. 其他
消除味道:一个重构应该是从识别一个坏味道(Bad Smell)开始,以消除一个坏味道结束,任何不以消除坏味道为目标的重构都是耍流氓。
始终工作:即重构定义中的“在不改变软件可观察行为的前提下”,说白了就是重构过程不能破坏甚至改变软件外在功能。
持续集成:不需要为重构单建分支,重构过程可以做到与开发分支在同一分支上持续集成持续交付。
随时中止:例如一个方法重命名,需要修改100个调用点,当改到50个的时候有个紧急的任务,我可以随时暂停重构,立即切换到Feature开发上,且不需要回滚已做的重构。
断点续传:还是上边的例子,假如我已经完成了紧急任务的开发,可以随时继续之前的重构,完成剩下50个调用点的重命名。
过程可逆:对于重构,经常有人会问:你怎么保证重构就会更好而不是更坏呢?重构的伟大就在于他跳出了对错之争,将关注点放到如何快速平滑安全的变化上,当然也包括反向重构。所以我的回答是:无法保证,但是我可以一分钟就重构回来。如果仔细看,《重构》书里的所有重构手法都是双向的,比如“提取函数”和“内联函数”。
好的重构应该像一边开车一边 换轮胎一样,保证系统随时可工作的前提下,还可以对其结构做出安全高效的调整。
重构是完全针对代码结构的行为,与代码的逻辑没有关系。
《重构》的第三章给出了22种代码的“坏味道”(在第二版里扩展成了24种),读者只需要在自己的代码中寻找这些不良的结构,然后遵循对应的重构手法一步步修改下去就行了,并不需要预先理解代码的逻辑——随着重构的进行,代码的逻辑自然会浮现出来。
迷思和误解
迷思和误解
这周重构下项目
重构是无数微小的、不改变软件行为的修改步骤组合,可以随时进行、随时停住,没有必要、也不应该集中做大块的重构。
不小心把代码重构坏了
重构的每个动作都应该是行为保持的(即:不改变软件可观察行为),严格说来,在重构过程中,软件应该每分钟都处于可工作的状态,根本不应该有“重构坏了”的状态出现。
整理好需求,才好重构
重构是完全针对代码结构的行为,只需要从坏味道入手、运用适当的重构手法,不需要预先了解代码的逻辑
没有银弹,重构技能需要刻意练习
只有通过刻意练习,掌握正确的重构操作方式,才能在各种复杂的真实情况中运用重构技巧,从而解开祖传代码的死结。
挑战:延缓新功能开发
代码所有权: 代码属于集体,集体对代码负责!
测试: 不该变程序可观察的行为,如何保证不出错?
注意:错误不可避免,自测试的代码可以快速发现错误并解决错误
重构与性能: 重构可能会使软件变慢,但它可以使软件的优化更容易!
自动化重构:
JetBrains 重构相关快捷键
提炼函数 Ctrl+alt+m
提炼变量 Ctrl+alt+v
生成测试 Ctrl+alt+t