2025年《重构:改善既有代码的设计》读书笔记(上)

《重构:改善既有代码的设计》读书笔记(上)第一章 重构第一个示例 第一章作者通过一个示例讲解重构的过程 由于需求变化或代码不易于理解需要进行重构 重构前需要有一个可靠的测试 重构的过程应该是小步修改 每次修改后就运行测试 测试过程中可以先忽略性能问题 1 重构过程 1 1 需求变化 是需求的变化使重构变得必要

大家好,我是讯享网,很高兴认识大家。

第一章:重构第一个示例

  第一章作者通过一个示例讲解重构的过程。由于需求变化或代码不易于理解需要进行重构,重构前需要有一个可靠的测试,重构的过程应该是小步修改,每次修改后就运行测试,测试过程中可以先忽略性能问题。

1. 重构过程

1.1 需求变化

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

1.2 重构的第一步——可靠的测试

  进行重构的时候,得确保即将修改的代码拥有一组可靠的测试。这些测试必须有自我检验能力。

1.3 小步修改,每次修改后就运行测试

  无论每次重构多么简单,养成重构后即运行测试的习惯非常重要。这就是重构过程的精髓所在:小步修改,每次修改后就运行测试。如果我改动了太多东西,犯错时就可能陷入麻烦的调试,并为此耗费大把时间。小步修改,以及它带来的频繁反馈,正是防止混乱的关键。

1.4 重构过程的性能问题

  大多数情况下可以忽略它。如果重构引入了性能损耗,先完成重构,再做性能优化。

1.5 好的命名

  好的命名十分重要,但往往并非唾手可得。但要一次把名取好并不容易,因此我会使用当下能想到最好的那个。如果稍后想到更好的,再将其换掉。

2. 结语

2.1 重构早期

  重构早期的主要动力是尝试理解代码如何工作。通常你需要先通读代码,找到一些感觉,然后再通过重构将这些感觉从脑海里搬回到代码中。清晰的代码更容易理解,使你能够发现更深层次的设计问题,从而形成积极正向的反馈环。

2.2 好代码的检验标准

  好代码的检验标准就是人们是否能轻而易举地修改它。好代码应该直截了当:有人需要修改代码时,他们应能轻易找到修改点,应该能快速做出更改,而不易引入其他错误。

2.3 好的代码库能提升生产力

  一个健康的代码库能够最大限度地提升我们的生产力,支持我们更快、更低成本地为用户添加新特性。为了保持代码库的健康,就需要时刻留意现状与理想之间的差距,然后通过重构不断接近这个理想。

2.4 高效有序的重构的关键

  小的步子可以更快前进,请保持代码永远处于可工作状态,小步修改累积起来也能大大改善系统的设计。

第二章:重构的原则

1. 何谓重构

1.1 何谓重构
  • 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
  • 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
    名词解析——结构调整:
  • “结构调整”(restructuring)来泛指对代码库进行的各种形式的重新组织或清理,重构则是特定的一类结构调整。
  • 名词解析——可观察行为:
    可观察行为”的意思是,整体而言,经过重构之后的代码所做的事应该与重构之前大致一样。用户应该关心的行为而言,不应该有任何改变。
1.2 重构的关键

  重构的关键在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成。

2. 两顶帽子的概念

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

2.1 添加新功能

  添加新功能时,我不应该修改既有代码,只管添加新功能。通过添加测试并让测试正常运行,我可以衡量自己的工作进度。

2.2 重构

  重构时我就不能再添加功能,只管调整代码的结构。此时我不应该添加任何测试(除非发现有先前遗漏的东西),只在绝对必要(用以处理接口变化)时才修改测试。

2.3 变换帽子

  软件开发过程中,可能会发现自己经常变换帽子。添加新功能和重构交替进行,无论何时都要清楚自己戴的是哪一顶帽子,并且明白不同的帽子对编程状态提出的不同要求。

3. 为何重构

3.1 重构改进软件的设计
3.1.1 程序在迭代中存在的问题

  如果没有重构,程序的内部设计(或者叫架构)会逐渐腐败变质。
  当人们只为短期目的而修改代码时,他们经常没有完全理解架构的整体设计,于是代码逐渐失去了自己的结构。程序员越来越难通过阅读源码来理解原来的设计。
  代码结构的流失有累积效应。越难看出代码所代表的设计意图,就越难保护其设计,于是设计就腐败得越快。

3.1.2 通过消除重复代码改善现有设计欠佳的程序
3.1.2 重构的好处

  经常性的重构有助于代码维持自己该有的形态。和优化程序设计。

3.3 重构帮助找到bug
3.4 重构提高编程速度
3.4.1 不注重重构的系统

  不注重重构的系统,一开始开发进展很快,随着版本的不断迭代,添加新功能需要花越来越多的时间去考虑如何把新功能塞进现有的代码库,不断蹦出来的bug修复起来也越来越慢。

  代码库看起来就像补丁摞补丁,需要细致的考古工作才能弄明白整个系统是如何工作的。这份负担不断拖慢新增功能的速度,到最后程序员恨不得从头开始重写整个系统。

下面这幅图可以描绘他们经历的困境。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFNU2Wse-1656737303567)(1E415CD640054B59960DAD8D908C6A9A)]
讯享网

3.4.2 经常重构的系统

  经常重构的系统他们添加新功能的速度越来越快,因为他们能利用已有的功能,基于已有的功能快速构建新功能。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nzJ59gxB-1656737303568)(8AF8EC6BC3B7464AB08F524139E977CC)]

3.4.3 两者的区别

  两种团队的区别就在于软件的内部质量。需要添加新功能时,内部质量良好的软件让我可以很容易找到在哪里修改、如何修改。良好的模块划分使我只需要理解代码库的一小部分,就可以做出修改。如果代码很清晰,我引入bug的可能性就会变小,即使引入了bug,调试也会容易得多。理想情况下,我的代码库会逐步演化成一个平台,在其上可以很容易地构造与其领域相关的新功能。

3.4.4 设计耐久性假说

通过投入精力改善内部设计,我们增加了软件的耐久性,从而可以更长时间地保持开发的快速。

3.4.5 重构改善已有程序设计

4. 何时重构

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

正如老话说的:事不过三,三则重构。

4.2 预备性重构:让添加新功能更容易
4.3 帮助理解的重构:使代码更易懂
4.4 捡垃圾式重构

  当我在重构过程中或者开发过程中,发现某一块不好,如果很容易修改可以顺手修改,但如果很麻烦,我又有紧急事情时候,可以选择记录下来(但不代表我就一点都做不到把他变好)。就像野营者的老话:至少让营地比你到达时更干净,久而久之,营地就非常干净(来自营地法则)

4.5 有计划的重构

  如果团队过去忽视了重构,那么常常会需要专门花一些时间来优化代码库,以便更容易添加新功能。在重构上花一个星期的时间,会在未来几个月里发挥价值。有时,即便团队做了日常的重构,还是会有问题在某个区域逐渐累积长大,最终需要专门花些时间来解决。但这种有计划的重构应该很少,大部分重构应该是不起眼的、见机行事的。

4.6 见机行事的重构

  重构经常发生在我们日常开发中,随手可改的地方。当我们发现不好的味道,就要将他重构。

4.7 长期重构

  可以在一个团队内,达成共识。每当有人靠近“重构区”的代码,就把它朝想要改进的方向推动一点。例如,如果想替换一个正在使用的库,可以先引入一层新的抽象,使其兼容新旧两个库的接口,然后一旦调用方完全改为了使用这层抽象,替换下面的库就会如容易的多。

4.8 复审代码时重构
4.9 何时不应该重构
  • 不需要修改的代码
  • 隐藏在一个API之下,只有当我需要理解其工作原理时,对其进行重构才有价值
  • 重写比重构还容易。

5. 重构的挑战

5.1 延缓新功能开发

  实际上,这只是一部分不理解重构真正原因的人的想法,重构是为了从长效上见到收益,一段优秀的代码能让我们开发起来更顺手,要权衡好重构与新功能的时机,比如一段很少使用的代码。就没必要对他重构

5.2 代码所有权

  有时候我们经常会遇到,接口发布者与调用者不是同一个人,并且甚至可能是用户与我们团队的区别,在这种情况下,需要使用函数改名手法,重构新函数,并且保留旧的对外接口来调用新函数,并且标记为不推荐使用。

5.3 分支的差异

  经常会有长期不合并的分支,一旦存在时间过长,合并的可能性就越低,尤其是在重构时候,我们经常要对一些东西进行改名和变化,所以最好还是尽可能短的进行合并,这就要求我们尽可能的将功能颗粒化,如果遇到还没开发完成且又无法细化的功能,我们可以使用特性开关对其隐藏。

5.4 缺乏一组自测试的代码

  一组好的测试代码对重构很有意义,它能让我们快速发现错误,虽然实现比较复杂,但他很有意义。

5.5 遗留代码

  不可避免,一组别人的代码使得我们很烦恼,如果是一套没有合理测试的代码则使得我们更加苦恼。这种情况下,我们需要增加测试,可以运用重构手法找到程序的接缝,再接缝处增加测试,虽然这可能有风险,但这是前进所必须要冒的风险,同时不建议一鼓作气的把整个都改完,更倾向于能够逐步地推进。

5.6 数据库

  数据库重构最好是分散到多次生产发布来完成,这样即便某次修改在生产数据库上造成了问题,也比较容易回滚。

6. 重构、架构和YAGNI

6.1 早期的经验

在任何人开始写代码之前,必须先完成软件的设计和架构。

6.2 存在的问题

  “在编码之前先完成架构”这种做法最大的问题在于,它假设了软件的需求可以预先充分理解。但经验显示,这个假设很多时候甚至可以说大多数时候是不切实际的。只有真正使用了软件、看到了软件对工作的影响,人们才会想明白自己到底需要什么。

6.3 通过重构改善架构

  重构改变了这种观点。有了重构技术,即便是已经在生产环境中运行了多年的软件,我们也有能力大幅度修改其架构。

6.4 应对未来需求变更

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

7. 重构与软件开发过程

7.1 徒有其名的敏捷开发
7.2 重构和软件开发过程

  三大实践:自测试代码、持续集成、重构

7.2.1 自测试代码

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

7.2.2 持续集成

  通过持续集成(CI)每个成员的重构都能快速分享给其他同事,不会发生这边在调用一个接口那边却已把这个接口删掉的情况;如果一次重构会影响别人的工作,我们很快就会知道。

7.3 真正的敏捷开发

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

8. 重构与性能

  重构可能使软件运行更慢,但它也使软件的性能优化更容易。除了对性能有严格要求的实时系统,其他任何情况下“编写快速软件”的秘密就是:先写出可调优的软件,然后调优它以求获得足够的速度。

第三章:代码的坏味道

1. 神秘命名(Mysterious Name)

2. 重复代码(Duplicated Code)

3. 过长函数(Long Function)

4. 过长参数列表(Long ParameterList)

5. 全局数据(Global Data)

6. 可变数据(Mutable Data)

7. 发散式变化(Divergent Change)

8. 霰弹式修改(Shotgun Surgery)

9. 依恋情结(Feature Envy)

10. 数据泥团(Data Clumps)

11. 基本类型偏执(PrimitiveObsession)

12. 重复的switch (RepeatedSwitches)

13. 循环语句(Loops)

14. 冗赘的元素(Lazy Element)

15. 夸夸其谈通用性(SpeculativeGenerality)

16. 临时字段(Temporary Field)

17. 过长的消息链(MessageChains)

18. 中间人(Middle Man)

19. 内幕交易(Insider Trading)

20. 过大的类(Large Class)

21. 异曲同工的类(AlternativeClasses with DifferentInterfaces)

22. 纯数据类(Data Class)

23. 被拒绝的遗赠(RefusedBequest)

24. 注释(Comments)

第四章:构筑测试体系

第五章:介绍重构名录

重构手法都有如下5个部分:

  • 名称(name):要建造一个重构词汇表。
    速写(sketch):帮助你更快找到你所需要的重构手法。
  • 动机(motivation):介绍“为什么需要做这个重构”和“什么情况下不该做这个重构”。
  • 做法(mechanics):简明扼要地一步一步介绍如何进行此重构。
  • 范例(examples):以一个十分简单的例子说明此重构手法如何运作。

第六章:第一组重构

1. 提炼函数(Extract Function)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHIBH0Z7-1656737303568)(AB58433A0DB94B8A9DDF4B2482584065)]

  • 曾用名:提炼函数(Extract Method)
  • 反向重构:内联函数
  • 时机:按照“将意图与实现分开”的原则,如果你需要花时间浏览一段代码才能弄清它到底在干什么,那么就应该将其提炼到一个函数中,并根据它所做的事为其命名。以后再读到这段代码时,你一眼就能看到函数的用途,大多数时候根本不需要关心函数如何达成其用途(这是函数体内干的事)。
  • 做法:
  1. 创造一个新函数,根据这个函数的意图来对它命名(以它“做什么”来命名,而不是以它“怎样做”命名)。
  2. 将待提炼的代码从源函数复制到新建的目标函数中。
  3. 仔细检查提炼出的代码,看看其中是否引用了作用域限于源函数、在提炼出的新函数中访问不到的变量。 若是,以参数的形式将它们传递给新函数。
  4. 所有变量都处理完之后,编译。
    在源函数中,将被提炼代码段替换为对目标函数的调用。
  5. 测试。
  6. 查看其他代码是否有与被提炼的代码段相同或相似之处。如果有,考虑使用以函数调用取代内联代码令其调用提炼出的新函数。
  • 关键字:新函数、拷贝、检查、作用域/上下文、编译、替换、修改细节。

作者的一个观点:
  一旦接受了“将意图与实现分开”这个原则,会逐渐养成一个习惯:写非常小的函数——通常只有几行的长度。
  小函数得有个好名字才行,所以你必须在命名上花心思。起好名字需要练习,不过一旦你掌握了其中的技巧,就能写出很有自描述性的代码。

2. 内联函数(Inline Function)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emeK9KG1-1656737303569)(FB60456AC75B4C2F8B86DCEB05387CFE)]

  • 曾用名:内联函数(Inline Method)
  • 反向重构:提炼函数
  • 时机:
  1. 内部代码和函数名称同样清晰易读。
  2. 手上有一群组织不甚合理的函数。可以先内联到一个大型函数中,再通过提炼函数合理重构。
  3. 代码中有太多简单委托的间接层。通过内联手法,去掉无用的间接层。
  • 做法:
  1. 检查函数,确定它不具多态性。
  2. 找出这个函数的所有调用点。
  3. 将这个函数的所有调用点都替换为函数本体。
  4. 每次替换之后,执行测试。
  5. 删除该函数的定义。
  • 关键字:检查多态、找调用并替换、删除定义。

3. 提炼变量(Extract Variable)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAk4dVRY-1656737303569)(BD91946F03F14502A5106B679303E11F)]

  • 曾用名:引入解释性变量(Introduce ExplainingVariable)
  • 反向重构:内联变量
  • 时机:
  1. 表达式非常复杂而难以阅读。
  2. 在多处地方使用这个值(可能是当前函数、当前类乃至于更大的如全局作用域)。
  • 做法:
  1. 确认要提炼的表达式没有副作用。
  2. 声明一个不可修改的变量,把你想要提炼的表达式复制一份,以该表达式的结果值给这个变量赋值。
  3. 用这个新变量取代原来的表达式。
  4. 测试。
  • 关键字:副作用、不可修改的变量、赋值、替换。

4. 内联变量(Inline Variable)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQzrnCEl-1656737303570)(C0E64B2079D64B87AB33706FDB8D8874)]

  • 曾用名:内联临时变量(Inline Temp)
  • 反向重构:提炼变量
  • 时机:
  1. 变量能给表达式提供有意义的名字,但这个名字并不比表达式本身更具表现力。
  2. 变量妨碍了重构附近的代码。
  • 做法:
  1. 检查确认变量赋值语句的右侧表达式没有副作用。
  2. 如果变量没有被声明为不可修改,先将其变为不可修改,并执行测试。
  3. 找到第一处使用该变量的地方,将其替换为直接使用赋值语句的右侧表达式。
  4. 测试。
  5. 重复前面两步,逐一替换其他所有使用该变量的地方。
  6. 删除该变量的声明点和赋值语句。
  7. 测试。
  • 关键字:副作用、只读、替换变量。

5. 改变函数声明(Change FunctionDeclaration)

在这里插入图片描述

  作者建议:最好能把大的修改拆成小的步骤,所以如果你既想修改函数名,又想添加参数,最好分成两步来做。(并且,不论何时,如果遇到了麻烦,请撤销修改,并改用迁移式做法。)。

  • 别名:函数改名(Rename Function)
  • 曾用名:函数改名(Rename Method)
  • 曾用名:添加参数(Add Parameter)
  • 曾用名:移除参数(Remove Parameter)
  • 别名:修改签名(Change Signature)
  • 时机:
  1. 函数名字不够贴切函数所做的事情。
  2. 函数参数增加。
  3. 函数参数减少。
  4. 函数参数概念发生变化。
  5. 函数因为某个参数导致的函数应用范围小(全局有很多类似的函数,在做着类似的事情)。
  • 做法:
    有两套:
  1. “简单的做法”:使用时机,变更的范围,能一步到位地修改函数声明及其所有调用者。
  2. 剩余情况用渐进的方式逐步迁移的做法。
  • 简单的做法(改变函数声明):
  1. 如果想要移除一个参数,需要先确定函数体内没有使用该参数。
  2. 修改函数声明,使其成为你期望的状态。
  3. 找出所有使用旧的函数声明的地方,将它们改为使用新的函数声明。
  4. 测试。
  • 迁移式做法:
  1. 如果有必要的话,先对函数体内部加以重构,使后面的提炼步骤易于开展。
  2. 使用提炼函数将函数体提炼成一个新函数。
  3. 如果提炼出的函数需要新增参数,用前面的简单做法添加即可。
  4. 测试。
  5. 对旧函数使用内联函数。
  6. 如果新函数使用了临时的名字,再次使用改变函数声明将其改回原来的名字。
  7. 测试。
  • 关键字:使用变量者、函数调用者、修改函数、声明改名、调用替换。

6. 封装变量(EncapsulateVariable)

  • 曾用名:自封装字段(Self-Encapsulate Field)
  • 曾用名:封装字段(Encapsulate Field)
    在这里插入图片描述
  • 时机:
  1. 当我们在修改或者增加使用可变数据的时候。
  2. 数据被大范围使用(设置值)。
  3. 对象、数组无外部变动需要内部一起改变的需求时候,最好返回一份副本。
  • 做法
  1. 创建封装函数,在其中访问和更新变量值。
  2. 执行静态检查。
  3. 逐一修改使用该变量的代码,将其改为调用合适的封装函数。每次替换之后,执行测试。
  4. 限制变量的可见性。
  5. 测试。
  6. 如果变量的值是一个记录,考虑使用封装记录。
  • 关键字:新函数、替换调用、不可见。

7. 变量改名(Rename Variable)在这里插入图片描述

  • 动机
  1. 变量/常量的名字不足以说明字段的意义。
  • 做法:
  1. 如果变量被广泛使用,考虑运用封装变量将其封装起来。
  2. 找出所有使用该变量的代码,逐一修改。
  3. 测试。

注意点:

  1. 如果在另一个代码库中使用了该变量,这就是一个“已发布变量”(published variable),此时不能进行这个重构。
  2. 如果变量值从不修改,可以将其复制到一个新名字之下,然后逐一修改使用代码,每次修改后执行测试。
  • 关键字:封装变量手法、替换名字、中间过渡

8. 引入参数对象(IntroduceParameter Object)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5HsQ87t3-1656737303571)(5FD43ED93C2B493D81E797417569EFB8)]

  • 时机:
  1. 一组数据项总是结伴同行。可以用一个数据结构代替。
  2. 函数参数过多。
  • 做法:
  1. 如果暂时还没有一个合适的数据结构,就创建一个。
  2. 测试。
  3. 使用改变函数声明给原来的函数新增一个参数,类型是新建的数据结构。
  4. 测试。
  5. 调整所有调用者,传入新数据结构的适当实例。每修改一处,执行测试。
  6. 用新数据结构中的每项元素,逐一取代参数列表中与之对应的参数项,然后删除原来的参数。测试。
  • 关键字:新结构、增加参数、入参新结构、删除旧参数、使用新结构。

9. 函数组合成类(CombineFunctions into Class)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJmKViyD-1656737303572)(8B9AE4E3A4534F0A8DD6250FA0AF49D7)]

  • 时机:
  1. 一组函数形影不离地操作同一块数据(通常是将这块数据作为参数传递给函数)。是时候组建一个类了,类能明确地给这些函数提供一个共用的环境,在对象内部调用这些函数可以少传许多参数,从而简化函数调用,并且这样一个对象也可以更方便地传递给系统的其他部分。
  • 做法:
  1. 运用封装记录对多个函数共用的数据记录加以封装。
  2. 对于使用该记录结构的每个函数,运用搬移函数将其移入新类。
  3. 用以处理该数据记录的逻辑可以用提炼函数提炼出来,并移入新类。
  • 关键字:提炼变量、封装成类、移入已有函数、替换调用、移入计算数据。

10. 函数组合成变换(CombineFunctions into Transform)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swtN04jC-1656737303572)(166D7E37A1FA4DBCABEB95306E9E0FC4)]

  • 时机:通过输入参数,获取到各种派生信息,这些派生数值会在几个不同地方用到,因此这些计算逻辑会在用到派生数据的地方重复。需要将计算派生数据的逻辑收拢到一处,这样始终可以在固定的地方找到和更新这些逻辑,避免到处重复。

有两种方案:

  1. 采用数据变换(transform)函数:这种函数接受源数据作为输入,计算出所有的派生数据,将派生数据以字段形式填入输出数据。有了变换函数,我就始终只需要到变换函数中去检查计算派生数据的逻辑。
  2. 函数组合成变换:先用源数据创建一个类,再把相关的计算逻辑搬移到类中。

作者对这两个方案点评:

  1. 这两个重构手法都很有用,我常会根据代码库中已有的编程风格来选择使用其中哪一个。
  2. 两者有一个重要的区别:
    • 如果代码中会对源数据做更新,那么使用类要好得多;
    • 如果使用变换,派生数据会被存储在新生成的记录中,一旦源数据被修改,我就会遭遇数据不一致。
  1. 我喜欢把函数组合起来的原因之一,是为了避免计算派生数据的逻辑到处重复。从道理上来说,只用提炼函数
    也能避免重复,但孤立存在的函数常常很难找到,只有把函数和它们操作的数据放在一起,用起来才方便。引入变换(或者类)都是为了让相关的逻辑找起来方便。
  • 做法:
  1. 创建一个变换函数,输入参数是需要变换的记录,并直接返回该记录的值。
  2. 挑选一块逻辑,将其主体移入变换函数中,把结果作为字段添加到输出记录中。修改客户端代码,令其使用这个新字段。
  3. 测试。
  4. 针对其他相关的计算逻辑,重复上述步骤。
  • 关键字:变换函数、变换入参、搬移计算逻辑。

11. 拆分阶段(Split Phase)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-19EZEvQe-1656737303572)(27758C58CC69453EB1114E3E9F4CF5AA)]

  • 时机:
  1. 一段代码在同时处理两件不同的事。
  • 做法:
  1. 将第二阶段的代码提炼成独立的函数。
  2. 测试。
  3. 引入一个中转数据结构,将其作为参数添加到提炼出的新函数的参数列表中。
  4. 测试。
  5. 逐一检查提炼出的“第二阶段函数”的每个参数。如果某个参数被第一阶段用到,就将其移入中转数据结构。每次搬移之后都要执行测试。
  • 关键字:拆分、提炼、独立的函数。
小讯
上一篇 2025-03-14 08:36
下一篇 2025-02-28 15:18

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/32309.html