【重构】读书笔记 1-6章

序言识别坏味道、测试先行、行为保持的变更动作,是重构的基本功。注面向对象、TDD、重构之类基本功者寥寥。以重构方式改进软件质量。重构的“十六字心法”,这十六字心法如是说:旧的不变, 新的创建, 一步切换, 旧的再见。什么是重构重构(refactoring)是这样一个过程: 在不改变代码外在行为的前提 下,对代码做出修改,以改进程序的内部结构。重构是一种经千锤百炼形成的有 条不紊的程序整理方法,可以最大限度地减小整理过程中引入错误的概率。本质上说,重构就是在代码写好之后改进它的设计。重构
摘要由CSDN通过智能技术生成

序言

《重构》的意义不只在于指导代码重构,更在于让人从一开始就知道什么是好的代码,并且尽量写出没有“坏味道”的代码。

识别坏味道、测试先行、行为保持的变更动作,是重构的基本功。

注面向对象、TDD、重构之类基本功者寥寥。

以重构方式改进软件质量。

重构的“十六字心法”,这十六字心法如是说:

  • 旧的不变
  • 新的创建
  • 一步切换
  • 旧的再见。

什么是重构

重构(refactoring)是这样一个过程:
在不改变代码外在行为的前提 下,对代码做出修改,以改进程序的内部结构。

重构是一种经千锤百炼形成的有 条不紊的程序整理方法,可以最大限度地减小整理过程中引入错误的概率。
本质上说,重构就是在代码写好之后改进它的设计。

重构的每个步骤都很简单,甚至显得有些过于简单:只需要把某个字段从一个类移到另一个类,把某些代码从一个函数拉出来构成另一个函数,或是在继承体系中把某些代码推上推下就行了。但是,聚沙成塔,这些小小的修改累积起来就可以根本改善设计质量。

本书有什么

第1章展示了一个小程序,其中有些常见的设计缺陷,我把它重构得更容易理解和修改。其间你可以看到重构的过程,以及几个很有用的重构手法。如果你想知道重构到底是怎么回事,这一章不可不读。

第2章讨论重构的一般性原则、定义,以及进行重构的原因,我也大致介绍 了重构面临的一些挑战。

第3章由Kent Beck介绍如何嗅出代码中的“坏味道”,以 及如何运用重构清除这些“坏味道”。

测试在重构中扮演着非常重要的角色,第4章介绍如何在代码中构筑测试。

从第5章往后的篇幅就是本书的核心部分——重构名录。尽管不能说是一份巨细靡遗的列表,却足以覆盖大多数开发者可能用到的关键重构手法。这份重构名录的源头是20世纪90年代后期我开始学习重构时的笔记,直到今天我仍然不时 查阅这些笔记,作为对我不甚可靠的记忆力的补充。每当我想做点什么——例如 拆分阶段(154)——的时候,这份列表就会提醒我如何一步一步安全前进。我 希望这是值得你日后一再回顾的部分。

  • 如果你想知道重构是什么,请阅读第1章,其中的示例会让你弄清楚重构的过程。
  • 如果你想知道为什么应该重构,请阅读前两章,它们会告诉你重构是什么以及为什么应该重构。
  • 如果你想知道该在什么地方重构,请阅读第3章,它会告诉你一些代码特征, 这些特征指出“这里需要重构”。
  • 如果你想着手进行重构,请完整阅读前四章,然后选择性地阅读重构名录。一 开始只需概略浏览列表,看看其中有些什么,不必理解所有细节。一旦真正需要实施某个重构手法,再详细阅读它,从中获取帮助。列表部分是供查阅的参考性内容,你不必一次就把它全部读完。

第1章 重构,第一个示例

如果你要给程序添加一个特性,但发现代码因缺乏良好的结构而不易 于进行更改,那就先重构那个程序,使其比较容易添加该特性,然后再添加该 特性。

我再强调一次,是需求的变化使重构变得必要。如果一段代码能正常工作, 并且不会再被修改,那么完全可以不去重构它。能改进之当然很好,但若没人需 要去理解它,它就不会真正妨碍什么。如果确实有人需要理解它的工作原理,并 且觉得理解起来很费劲,那你就需要改进一下代码了。

1.3 重构的第一步

每当我要进行重构的时候,第一个步骤永远相同:**我得确保即将修改的代码 拥有一组可靠的测试。**这些测试必不可少,因为尽管遵循重构手法可以使我避免 绝大多数引入bug的情形。

重构前,先检查自己是否有一套可靠的测试集。这些测试必须有自我 检验能力。

进行重构时,我需要依赖测试。我将测试视为bug检测器,它们能保护我不 被自己犯的错误所困扰。把我想要达成的目标写两遍——代码里写一遍,测试里 再写一遍——我就得犯两遍同样的错误才能骗过检测器。这降低了我犯错的概 率,因为我对工作进行了二次确认。尽管编写测试需要花费时间,但却为我节省 下可观的调试时间。构筑测试体系对重构来说实在太重要了。

无论每次重构多么简单,养成重构后即运行测试的习惯非常重要。

做完一次修改就运行测试,这样在 我真的犯了错时,只需要考虑一个很小的改动范围,这使得查错与修复问题易如 反掌。这就是重构过程的精髓所在:小步修改,每次修改后就运行测试。如果我 改动了太多东西,犯错时就可能陷入麻烦的调试,并为此耗费大把时间。小步修 改,以及它带来的频繁反馈,正是防止混乱的关键。

提炼函数是一个常见的可自动完成的重构。如果我是用Java编程,我 会本能地使用IDE的快捷键来完成这项重构。

变量命名是代码清晰的关键。只要改名能够提升代码的可读性,那就应 该毫不犹豫去做。有好的查找替换工具在手,改名通常并不困难。

这里我要使 用的重构手法是以查询取代临时变量(178)。

1.4 结语

好代码的检验标准就是人们是否能轻而易举地修改它。

开展高效有序的重构,关键的心得是:小的步子可以更快前进,请保持代码永远处于可工作状态,小步修改累积起来也能大大改善系统的设计。

第2章 重构的原则

2.1 何谓重构

重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

重构的关键在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成。因此,在重构的过程中,我的代码很少进入不可工作的状态,即便重构没有完成,我也可以在任何时刻停下来。

我会用“结构调整”(restructuring)来泛指对代码库进行的各种形式的重新组织或清理,重构则是特定的一类结构调整。刚接触重构的人看我用很多小步骤完成似乎可以一大步就能做完的事,可能会觉得这样很低效。但小步前进能让我走得更快,因为这些小步骤能完美地彼此组合,而且——更关键的是——整个过程中我不会花任何时间来调试。

重构与性能优化有很多相似之处:两者都需要修改代码,并且两者都不会改变程序的整体功能。
两者的差别在于其目的:重构是为了让代码“更容易理解,更易于修改”。这可能使程序运行得更快,也可能使程序运行得更慢。在性能优
化时,我只关心让程序运行得更快,最终得到的代码有可能更难理解和维护,对此我有心理准备。

2.2 两顶帽子

Kent Beck提出了“两顶帽子”的比喻。使用重构技术开发软件时,我把自己的时间分配给两种截然不同的行为:
添加新功能和重构。

  • 添加新功能时,我不应该修改既有代码,只管添加新功能。通过添加测试并让测试正常运行,我可以衡量自己的工作进度。
  • 重构时我就不能再添加功能,只管调整代码的结构。此时我不应该添加任何测试,只在绝对必要时才修改测试。

软件开发过程中,我可能会发现自己经常变换帽子。首先我会尝试添加新功能,然后会意识到:如果把程序结构改一下,功能的添加会容易得多。于是我换一顶帽子,做一会儿重构工作。程序结构调整好后,我又换上原先的帽子,继续添加新功能。

2.3 为何重构

重构改进软件的设计

  • 经常性的重构有助于代码维持自己该有的形态。

重构使软件更容易理解

重构帮助找到bug

重构提高编程速度

  • 需要添加新功能时,内部质量良好的软件让我可以很容易找到在哪里修改、如何修改。良好的模块划分使我只需要理解代码库的一小部分,就可以做出修改。如果代码很清晰,我引入bug的可能性就会变小,即使引入了bug,调试也会容易得多。

2.4 何时重构

三次法则:

第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,你就应该重构。正如老话说的:事不过三,三则重构。

预备性重构:让添加新功能更容易

重构的最佳时机就在添加新功能之前。在动手添加新功能之前,我会看看现有的代码库,此时经常会发现:如果对代码结构做一点微调,我的工作会容易得多。

帮助理解的重构:使代码更易懂

优秀的程序员知道,添加新功能最快的方法往往是先修改现有的代码,使新功能容易被加入。

复审代码时重构

代码复审有助于在开发团队中传播知识,也有助于让较有经验的开发者把知识传递给比较欠缺经验的人,并帮助更多人理解大型软件系统中的更多部分。

怎么对经理说

我领这份工资,是因为我擅长快速实现新功能;我认为最快的方式就是重构,所以我就重构。

2.5 重构的挑战

重构的唯一目的就是让我们开发更快,用更少的工作量创造更大的价值。

我有时会看到一个(大规模的)重构很有必要进行,而马上要添加的功能非常小,这时我会更愿意先把新功能加上,然后再做这次大规模重构。做这个决定需要判断力。

重构的意义不在于把代码库打磨得闪闪发光,而是纯粹经济角度出发的考量。我们之所以重构,因为它能让我们更快——添加功能更快,修复bug更快。

应该尽量缩短特性分支的生存周期,比如只有一两天。还有一些人(比如我本人)认为特性分支的生命还应该更短,我们采用的方法叫作持续集成(Continuous Integration,CI),也叫“基于主干开发”(Trunk-Based Development)。在使用CI时,每个团队成员每天至少向主线集成一次。这个实践避免了任何分支彼此差异太大,从而极大地降低了合并的难度。

关键就在于“快速发现错误”。要做到这一点,我的代码应该有一套完备的测试套件,并且运行速度要快,否则我会不愿意频繁运行它。也就是说,绝大多数情况下,如果想要重构,我得先有可以自测试的代码。

2.6 重构、架构和YAGNI

重构对架构最大的影响在于,通过重构,我们能得到一个设计良好的代码库,使其能够优雅地应对不断变化的需求。

有了重构技术,我就可以采取不同的策略。与其猜测未来需要哪些灵活性、需要什么机制来提供灵活性,我更愿意只根据当前的需求来构造软件,同时把软件的设计质量做得很高。随着对用户需求的理解加深,我会对架构进行重构,使其能够应对新的需要。

2.7 重构与软件开发过程

要真正以敏捷的方式运作项目,团队成员必须在重构上有能力、有热情,他们采用的开发过程必须与常规的、持续的重构相匹配。

重构的第一块基石是自测试代码。我应该有一套自动化的测试,我可以频繁地运行它们,并且我有信心如果我在编程过程中犯了任何错误,会有测试失败。

如果一支团队想要重构,那么每个团队成员都需要掌握重构技能,能在需要时开展重构,而不会干扰其他人的工作。这也是我鼓励持续集成的原因:有了CI,每个成员的重构都能快速分享给其他同事,不会发生这边在调用一个接口那边却已把这个接口删掉的情况;如果一次重构会影响别人的工作,我们很快就会知道。自测试的代码也是持续集成的关键环节,所以这三大实践——自测试代码、持续集成、重构——彼此之间有着很强的协同效应。

**持续交付确保软件始终处于可发布的状态,**很多互联网团队能做到一天多次发布,靠的正是持续交付的威力。即便我们不需要如此频繁的发布,持续集成也能帮我们降低风险,并使我们做到根据业务需要随时安排发布,而不受技术的局限。有了可靠的技术根基,我们能够极大地压缩“从好点子到生产代码”的周期时间,从而更好地服务客户。这些技术实践也会增加软件的可靠性,减少耗费在bug上的时间。

2.8 重构与性能

“编写快速软件”的秘密就是:先写出可调优的软件,然后调优它以求获得足够的速度。

第3章 代码的坏味道

我们只会告诉你一些迹象,它会指出“这里有一个可以用重构解决的问题”。你必须培养自己的判断力,学会判断一个类内有多少实例变量算是太大、一个函数内有多少行代码才算太长。

3.1 神秘命名(Mysterious Name)

整洁代码最重要的一环就是好的名字,所以我们会深思熟虑如何给函数、模块、变量和类命名,使它们能清晰地表明自己的功能和用法。

然而,很遗憾,命名是编程中最难的两件事之一。正因为如此,改名可能是最常用的重构手法,包括改变函数声明(124)(用于给函数改名)、变量改名(137)、字段改名(244)等。

3.2 重复代码(Duplicated Code)

如果你在一个以上的地点看到相同的代码结构,那么可以肯定:设法将它们合而为一,程序会变得更好。一旦有重复代码存在,阅读这些重复的代码时你就必须加倍仔细,留意其间细微的差异。如果要修改重复代码,你必须找出所有的副本来修改。

最单纯的重复代码就是“同一个类的两个函数含有相同的表达式”。这时候你需要做的就是采用提炼函数(106)提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码。如果重复代码只是相似而不是完全相同,请首先尝试用移动语句(223)重组代码顺序,把相似的部分放在一起以便提炼。如果重复的代码段位于同一个超类的不同子类中,可以使用函数上移(350)来避免在两个子类之间互相调用。

3.3 过长函数(Long Function)

活得最长、最好的程序,其中的函数都比较短。

间接性带来的好处——更

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值