《重构2》读书笔记
2022-05-29 11:39:22 0 举报
AI智能生成
《重构2》
作者其他创作
大纲/内容
第8章 搬移特性
第9章 重新组织数据
第10章 简化条件逻辑
第11章 重构API
11.2 函数参数化(Parameterize Function)
第12章 处理继承关系
其他
参考文献
索引
第1章 重构,第一个示例
1.1 起点
1.2 对此起始程序的评价
若程序杂乱无章,先为它整理出结构来,再做需要的修改,通常更简单
再强调一次,是需求的变化使重构变得必要
1.3 重构的第一步
重构前,确认有一套可靠的测试集,这些测试必须有自我检验能力
编写测试需要花费时间,但却可节省可观的调试时间
1.4 分解 statement 函数
重构过程的精髓:小步修改,每次修改后就运行测试
修改->测试->提交到本地版本控制系统:每次微小的重构后都会提交代码,以便在搞砸后,轻松回滚到上一
可工作状态,把代码推送(push)到远程仓库前,我会把零碎的修改压缩成一个更有意义的提交(commit)
可工作状态,把代码推送(push)到远程仓库前,我会把零碎的修改压缩成一个更有意义的提交(commit)
这是我个人的编码风格:永远将函数返回值命令名result,这样我一眼就能知道它的作用
这是我另一个编码风格:使用一门动态类型语言,参数取名时都默认带上其类型名,这习惯从Kent Beck学来
移除play变量
移除局部变量的好处是做提炼是会简单得多,因为需要操心的局部作用域变少了
实际上,在做任何提炼前,我一般都会先移除局部变量
移除format变量
只有恰如其分地命名,才能彰显出将大函数分解成小函数的价值
但要一次把名取好并不容易,因此我会使用当下能想到最好的那个
如果稍后发现有更好的,我将毫不犹豫地换掉它
通常你需要花几分钟通读更多代码,才能发现最好的名称是什么
如果稍后发现有更好的,我将毫不犹豫地换掉它
通常你需要花几分钟通读更多代码,才能发现最好的名称是什么
移除观众量积分之和
把与更新volumeCredits变量相关的代码都集中到一起,有利于"以查询取代临时变量(178)"手法的施展
软件的性能通常只与一小部分相关,改变其他的部分往往对总体性能贡献甚微
有些,一些重构手法也会显著影响性能,我通常也不管它,继续重构,因为有一份结构良好的代码,回头调优也容易得多
重构后再花时间调化,调优时可能会回退早先做的一些重构,但更多时间,因为重构可使用更高效的调优方案。
特别是与复杂代码打交道时,细小的步子是快速前进的关键
1.5 进展:大量嵌套函数
所一整块代码拆解成大嵌套函数可以使代码结构更清晰
1.6 拆分计算阶段与格式化阶段
重构早期的一般步骤:拆解代码块,方便更好的理解它
把复杂的代码块分解为更小的单元,与好的命名一样重要
1.7 进展:分离到两个文件(和两个阶段)
将代码抽取到函数会带来额外包装成本:代码行数可能变多,但重构也提高了代码可读性
虽说言以简为贵,但可演化的软件却以明确为贵
编程时,需遵循营地法则:保证你离开时的代码库一定比你来时更健康
1.8 按类型重组计算过程
1.9 进展:使用多态计算器来提供数据
1.10 总结
第2章 重构的原则
2.1 何谓重构
重构(名词):对软件内部结构的一种调整,不改变软件可观察行为前提下,提高可理解性,降低修改成本
重构(动词):使用一系列重构手法,不改变“软件可观察行为”前提下,调整其结构
重构的关键在于运用大量微小且保持软件行为的步骤,一步步达成大规模修改
结构调整(Restructuring)和重构(Refactoring)是有区分的
若有人说他们的代码在重构时有一两天不可用,他们不是在重构!因为重构是行为保持的!
2.2 两顶帽子
Kent Beck提出了“两顶帽子”的比喻:添加新功能 & 重构
添加新功能时:不应该修改即有代码;通过添加测试并让测试正常运行
重构时:不能再添加功能,只调整代码结构;若非绝对必要,不应该添加任何测试
无论何时都应该清楚自己戴的是哪一顶帽子,且明白不同的帽子对编程状态提出的不同要求
2.3 为何重构
改进软件的设计
消除重复代码
使软件更容易理解
代码不仅是给计算机读的,最重要的是代码也是给人读的
帮助找到bug
Kent Beck经常形容自己:我不是一个特别好的程序员,我只是一个有着一些特别好的习惯的还不错的程序员
提高编程速度
添加新功能时,内部质量良好的软件让我可以容易找到在哪修改,如何修改
良好的模块划分使我只需理解代码库一小部分即可做出修改
若代码清晰,引入bug的可能会变小,即使引入了bug,调试也容易很多
设计耐久性假说
2.4 何时重构
三次法则
事不过三,三则重构
预备性重构:让添加新功能更容易
重构的最佳时机就在添加新功能之前
帮助理解的重构:使代码更易懂
捡垃圾式重构
帮助理解的重构的一个变体
有计划的重构和见机行事的重构
长期重构
怎么对经理说
何时不应该重构
2.5 重构的挑战
延缓新功能开发
重构的唯一目的就是让我们开发更快,用更少的工作量创造最大的价值
代码所有权
代码所有权的边界会妨碍重构
推荐团队代码所有制,每个成员都可修改此团队拥有的代码
跨团队代码所有制,有些团队鼓励类似于开源的模型:pull request
分支(版本控制)
合并与集成是两回事
合并(Merge):从主干合并到我的分支,这是一个单向代码移动,我的分支变更了,但主干没有
集成(Integrate):双向过程,不仅把主干pull到我的分支,还要把我分支的修改push回主干
好的实践是:持续集成(Continuous Integration, CI),也叫"基于主干开发"(Trunk-Based Development)
使用CI时,每个团队成员每天至少向主干集成一次
需要有相关实践确保主干随时处于健康状态,要学会:
大功能拆分成小块
使用特性开关(feature toggle)
CI可以降低分支合并难度,但最重要的原因还是CI与重构能良好配合,所以Kent Beck在极限编程中同时包含了这两个实践
测试
不会改变程序可观察的行为,这是重构的一个重要特征
想要重构,先要有可以"自测试的代码":测试套件运行速度要快(否则我不愿意频繁运行它),用于"快速发现错误"(重构可能引起错误)
自测试代码与持续集成紧密相关,我们仰赖CI及时捕获分支集成时的语义冲突。自测试代码是极限编程另一重要组成部分,也是CI的关键环节
遗留代码
此问题无简单解决之法,我能给出的最好建议是买一本《修改代码的艺术》,照书中指导来做。别担心书太老,尽管已出版十多年,其中建议仍管用
一言以蔽之,先找到程序的接缝,在接缝处插入测试,如此将系统置于测试覆盖之下
不建议尝试一鼓作气把复杂混乱的遗留代码重构成漂亮的代码,每次一小块,一点点来
数据库
Pramod Sadalage发展出一套渐进式数据库设计和数据库重构的办法,已被广泛使用
精要:借助数据迁移脚本,将DB结构的修改与代码相结合
与通常的重构一样,数据库重构的关键点也是小步修改且每次修改都应该完整
并行修改(Parallel Change)
先加一个新字段,数据同时写入新旧字段,调用处也修改使用新字段
暂停一小段时间,观察若无bug,再删除无人使用的旧字段
2.6 重构、架构和YAGNI
重构极大的改变了人们考虑软件架构的方式
错误:写代码前,先完成软件设计和架构,一旦代码写出来,架构就固定了,逐渐腐败
正确:有了重构技术,就有能力大幅度修改生产环境运行多年的软件架构
三大软件原则
1. 不做重复的事 | DRY(Don't Repeat Yourself)
2. 保持简单直接 | KISS(Keep it Simple Stupid)
3. 你不需要它 | YAGNI(You Ain’t Gonna Need It)
YAGN并非"不做架构性思考"
YAGNI是架构、设计与开过过程融合的一种工作方式,此工作方式需有重构为基础才可靠
2.7 重构与软件开发过程
重构的第一块基石是自测试代码,自测试代码也是持续集成的关键环节
三大实践:自测试代码,持续集成,重构
有这三大核心实践打下的基础,才谈得上运用敏捷思想的其他部分
2.8 重构与性能
不赞成为了提高设计纯洁性而忽视性能,重构可能影响性能,但它也使性能优化更容易
"编写快速软件(快速指:性能好)"的秘密是:先写出可调优的软件,再调优
3种性能提升法
第1种:时间预算法 | 用于心律调节器之类高度重视性能的系统,但企业信息系统追求如此高性能就过分了
第2种:持续关注法 | 此法作用不大,因为90%的优化工作都是白费劲
第3种:发现热点,去除热点 | 使用度量工具监控程序运行,在较小粒度范围内定位性能问题并解决
2.9 重构起源何处
重构(refactoring)一词的真正起源已经无从知晓了
最早认识重构重要性的两人是Ward Cunningham和Kent Beck
Smalltalk的“编译-链接-执行”周期非常短,容易快速修改代码,特别适合重构
Ralph Johnson(GoF之一)是Smalltalk社区另一位领袖
Ralph Johnson的博士研究生学生Bill Opdyke的博士论文是重构领域第一部丰硕的研究成果
John Brant和Don Roberts 开发了第一个自动化重构工具:Refactoring Browser(重构浏览器)
重构的概念已被行业广泛接受,但"重构"一词也被滥用,也许只是做了无视行为保持的不严谨的结构调整
Martin Fowler写了《重构1》和《重构2》
2.10 自动化重构
IDE的Refactor功能
重构工具背后的原则
粗糙的文本操作:查找 / 替换
IDE既能处理文本,还能处理语法树:IDE比文本编辑器更先进的地方
静态类型语言(如:JAVA)的重构可以完全安全,完全自动
2.11 延展阅读
有大量关于重构的材料已超本书范围
本书关注点是组织一本重构参考书,若需入门教材,推荐Bill Wake的《重构手册》,有很多重构练习
Josh Kerievsky的《重构与模式》紧密连接了“重构”和“软件模式”这两个世界
本书聚集通用编程语言重构技巧,还有一些专门领域的重构
《数据库重构》| Scott Ambler & Pramod Sadalage
《重构HTML》| Elliotte Rusty Harold
《修改代码的艺术》| Michael Feathers
主要讨论如何在缺乏测试覆盖的老旧代码库上开展重构
重构网站:https://refactoring.com
第3章 代码的坏味道
大牛说重构
熊节
对于烂代码,提炼函数和改名两招解决八成
仝键
其实这两招(提炼函数和改名)是最难的,重构界的蛋炒饭
凉粉小刀
尤其是改名(最难搞)
3.1 神秘命名(Mysterious Name)
重构手法
改变函数声明(124)
变量改名(137)
字段改名(244)
好名字是整洁代码最重要一环
整洁代码(clean code)最重要的一环就是好的名字:用于清晰表达功能和用法
好名字节省猜谜时间
为程序元素改名,好的名字可以节省未来用在猜谜上的大把时间
坏名字潜藏设计问题
想不出一个好名字,说明背后可能潜藏更深的设计问题
改名是最常用重构手法
然而,很遗憾,命名是编程中最难的两件事之一:因此,改名是最常用重构手法
3.2 重复代码(Duplicated Code)
重构手法
提炼函数(106)
移动语句(223)
函数上移(350)
3.3 过长函数(Long Function)
重构手法
提炼函数(106)
99%的场合,用此招把函数变短
拆分循环(227)
条件表达式
分解条件表达式(260)
以多态取代条件表达式(272)
大量参数和临时变量阻碍"提炼函数"
以查询取代临时变量(178)
引入参数对象(140)
保持对象完整(319)
以命令取代函数(337)
短函数
活得最长最好
更好的阐释力,更易于分享,更多的选择
早期编程语言中子程序调用要额外开销,现代编程语言没这个问题了
函数命名原则
小函数易于理解的关键在于好名字,见名知义,无需关心其实现代码
以其用途,而非实现手法进行命名
哪怕函数调用动作比函数自身还长,若函数名能解释其用途,也要毫不犹豫地用长名字<br>关键不在函数长度,在于函数“做什么”和“如何做”之间的语义距离
长函数
函数越长,越难理解
何处提炼
需要写注释的地方
如果要写注释,就应该提炼到一个独立函数中,哪怕只有一行代码;用命名替代注释
有条件表达式和循环的地方
第4章 构筑测试体系
第5章 介绍重构名录
第6章 第一组重构
第7章 封装
0 条评论
下一页