代码重构该怎么办呢

简介: 《系统设计》系列

前段时间重读了《重构:改善代码既有设计》open in new window,收货颇多。于是,简单写了一篇文章来聊聊我对重构的看法

image.png

何谓重构?

学习重构必看的一本神书《重构:改善代码既有设计》从两个角度给出了重构的定义:

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

用更贴近工程师的语言来说: 重构就是利用设计模式(如组合模式、策略模式、责任链模式)、软件设计原则(如 SOLID 原则、YAGNI 原则、KISS 原则)和重构手段(如封装、继承、构建测试体系)来让代码更容易理解,更易于修改。

软件设计原则指导着我们组织和规范代码,同时,重构也是为了能够尽量设计出尽量满足软件设计原则的软件

正确重构的核心在于 步子一定要小,每一步的重构都不会影响软件的正常运行,可以随时停止重构

常见的设计模式如下

image.png

更全面的设计模式总结,可以看 java-design-patterns 这个开源项目。

常见的软件设计原则如下

9.png

更全面的设计原则总结,可以看 java-design-patternsopen in new windowhacker-laws-zh 这两个开源项目

为什么要重构?

在上面介绍重构定义的时候,我从比较抽象的角度介绍了重构的好处:重构的主要目的主要是提升代码&架构的灵活性/可扩展性以及复用性。

如果对应到一个真实的项目,重构具体能为我们带来什么好处呢?

  1. 让代码更容易理解 : 通过添加注释、命名规范、逻辑优化等手段可以让我们的代码更容易被理解;
  2. 避免代码腐化 :通过重构干掉坏味道代码;
  3. 加深对代码的理解 :重构代码的过程会加深你对某部分代码的理解;
  4. 发现潜在 bug :是这样的,很多潜在的 bug ,都是我们在重构的过程中发现的;
  5. ......

看了上面介绍的关于重构带来的好处之后,你会发现重构的最终目标是 提高软件开发速度和质量 。

重构并不会减慢软件开发速度,相反,如果代码质量和软件设计较差,当我们想要添加新功能的话,开发速度会越来越慢。到了最后,甚至都有想要重写整个系统的冲动


《重构:改善代码既有设计》这本书中这样说

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

何时进行重构?

重构在是开发过程中随时可以进行的,见机行事即可,并不需要单独分配一两天的时间专门用来重构

提交代码之前

《重构:改善代码既有设计》这本书介绍了一个 营地法则 的概念:

编程时,需要遵循营地法则:保证你离开时的代码库一定比来时更健康。

这个概念表达的核心思想其实很简单:在你提交代码的之前,花一会时间想一想,我这次的提交是让项目代码变得更健康了,还是更腐化了,或者说没什么变化?

项目团队的每一个人只有保证自己的提交没有让项目代码变得更腐化,项目代码才会朝着健康的方向发展。

当我们离开营地(项目代码)的时候,请不要留下垃圾(代码花味道)!尽量确保营地变得更干净了!

开发一个新功能之后&之前

在开发一个新功能之后,我们应该回过头看看是不是有可以改进的地方。在添加一个新功能之前,我们可以思考一下自己是否可以重构代码以让新功能的开发更容易。

一个新功能的开发不应该仅仅只有功能验证通过那么简单,我们还应该尽量保证代码质量。

有一个两顶帽子的比喻:在我开发新功能之前,我发现重构可以让新功能的开发更容易,于是我戴上了重构的帽子。重构之后,我换回原来的帽子,继续开发新能功能。新功能开发完成之后,我又发现自己的代码难以理解,于是我又戴上了重构帽子。比较好的开发状态就是就是这样在重构和开发新功能之间来回切换

image.png

Code Review 之后

Code Review 可以非常有效提高代码的整体质量,它会帮助我们发现代码中的坏味道以及可能存在问题的地方。并且, Code Review 可以帮助项目团队其他程序员理解你负责的业务模块,有效避免人员方面的单点风险。

经历一次 Code Review ,你的代码可能会收到很多改进建议。

捡垃圾式重构

当我们发现坏味道代码(垃圾)的时候,如果我们不想停下手头自己正在做的工作,但又不想放着垃圾不管,我们可以这样做:

  • 如果这个垃圾很容易重构的话,我们可以立即重构它。
  • 如果这个垃圾不太容易重构的话,我们可以先记录下来,当完成当下的任务再回来重构它

阅读理解代码的时候

搞开发的小伙伴应该非常有体会:我们经常需要阅读项目团队中其他人写的代码,也经常需要阅读自己过去写的代码。阅读代码的时候,通常要比我们写代码的时间还要多很多。

我们在阅读理解代码的时候,如果发现一些坏味道的话,我们就可以对其进行重构。

就比如说你在阅读张三写的某段代码的时候,你发现这段代码逻辑过于复杂难以理解,你有更好的写法,那你就可以对张三的这段代码逻辑进行重构

重构有哪些注意事项?

单元测试是重构的保护网

单元测试可以为重构提供信心,降低重构的成本。我们要像重视生产代码那样,重视单元测试。

另外,多提一句:持续集成也要依赖单元测试,当持续集成服务自动构建新代码之后,会自动运行单元测试来发现代码错误。

怎样才能算单元测试呢? 网上的定义很多,很抽象,很容易把人给看迷糊了。我觉得对于单元测试的定义主要取决于你的项目,一个函数甚至是一个类都可以看作是一个单元。就比如说我们写了一个计算个人股票收益率的方法,我们为了验证它的正确性专门为它写了一个单元测试。再比如说我们代码有一个类专门负责数据脱敏,我们为了验证脱敏是否符合预期专门为这个类写了一个单元测试

单元测试也是需要重构或者修改的。 《代码整洁之道:敏捷软件开发手册》open in new window这本书这样写到

测试代码需要随着生产代码的演进而修改,如果测试不能保持整洁,只会越来越难修改

不要为了重构而重构

重构一定是要为项目带来价值的! 某些情况下我们不应该进行重构:

  • 学习了某个设计模式/工程实践之后,不顾项目实际情况,刻意使用在项目上(避免货物崇拜编程);
  • 项目进展比较急的时候,重构项目调用的某个 API 的底层代码(重构之后对项目调用这个 API 并没有带来什么价值);
  • 重写比重构更容易更省事;
  • ......

遵循方法

《重构:改善代码既有设计》这本书中列举除了代码常见的一些坏味道(比如重复代码、过长函数)和重构手段(如提炼函数、提炼变量、提炼类)。我们应该花时间去学习这些重构相关的理论知识,并在代码中去实践这些重构理论。

如何练习重构?

除了可以在重构项目代码的过程中练习精进重构之外,你还可以有下面这些手段:

  • 重构实战练习:通过几个小案例一步一步带你学习重构!
  • 设计模式+重构学习网站 :免费在线学习代码重构、 设计模式、 SOLID 原则 (单一职责、 开闭原则、 里氏替换、 接口隔离以及依赖反转) 。
相关文章
|
安全 Shell Linux
【Shell 命令集合 系统设置 】Linux 初始化系统设置setup命令 使用指南
【Shell 命令集合 系统设置 】Linux 初始化系统设置setup命令 使用指南
296 0
|
存储 缓存 前端开发
两种异步日志方案的介绍
两种异步日志方案的介绍
439 0
|
存储 数据可视化 定位技术
如何高效管理远程团队?2024年最佳免费工作协作工具排行榜
随着远程工作日益普遍,选择合适的协作工具成为提升团队效率的关键。本文介绍了7款优秀的免费远程协作工具,包括板栗看板、Wrike、Miro、ClickUp、Zoho Projects、MeisterTask和Quire,它们各自具备独特优势,如项目管理、任务跟踪、文件共享及实时协作等功能,适用于不同规模和需求的团队,帮助实现高效远程工作。
如何高效管理远程团队?2024年最佳免费工作协作工具排行榜
IDEA自定义配置注释模板,让你看起来更加专业!!!
IDEA自定义配置注释模板,让你看起来更加专业!!!
1730 0
|
缓存 网络协议 Linux
通过实验深入了解 TCP 连接的建立和关闭
TCP/IP 这个主题很多文章比较陈旧,且以讹传讹的东西太多,所以本文作者结合了理论和实践去写,旨在通过一系列实验帮助读者深入理解 TCP 连接的建立过程。
507 13
|
设计模式 安全 Java
老系统重构系列--如何用一套流程接入所有业务线
**摘要:** 本文介绍了老系统改造的过程,作者提出,ToB业务的挑战在于需要支持多种差异化的业务需求,而模板模式在处理这种需求时可能会导致继承关系复杂和粒度过粗。为了解决这些问题,文章提出了以下步骤: 1. **梳理流程差异点**:识别不同业务流程的差异,以便确定扩展点。 2. **领域模型梳理**:区分核心域和支撑域,确保核心域的稳定性。 3. **二次抽象隔离层**:创建隔离层,避免核心域因新业务接入而变得不稳定。 4. **基于SPI的扩展体系建设**:选择了COLA-SPI实现扩展点,允许业务域定义接口并实现差异化的流程逻辑。
667 0
|
Ubuntu Linux 开发工具
Linux下多窗口分屏式终端--Terminator
Linux下多窗口分屏式终端--Terminator
1923 0
Linux下多窗口分屏式终端--Terminator
|
算法 Java 测试技术
mybatis-plus雪花算法生成Id使用详解
mybatis-plus雪花算法生成Id使用详解
5037 0
mybatis-plus雪花算法生成Id使用详解
|
机器学习/深度学习 人工智能 自然语言处理
大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型应用以及各个版本之间的区别详解
大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型应用以及各个版本之间的区别详解
大语言模型的预训练[2]:GPT、GPT2、GPT3、GPT3.5、GPT4相关理论知识和模型实现、模型应用以及各个版本之间的区别详解
|
Java API 项目管理
Java一分钟之-Gradle插件开发:自定义构建逻辑
【6月更文挑战第5天】Gradle插件开发详解:从入门到发布。文章介绍如何创建自定义插件,强调依赖管理、任务命名和配置阶段的理解。示例代码展示插件实现及避免常见问题的方法。最后,讨论插件的发布与共享,助你提升构建效率并贡献于开发者社区。动手实践,打造强大Gradle插件!
372 3

热门文章

最新文章