读《重构:改善既有代码的设计》的时候,部门最近正好在做项目.Net9.0升级,因此对重构这个概念有了一些理解。
何谓重构(What)
作者对重构是这样定义的:“重构”是对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。通过使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
用简单的话来讲,不是增加新功能,而是让已有的代码变得更整洁、更易于理解和修改。同时在重构的过程中,作者提出必须让代码始终处于“可工作”状态。这就像重新装修一间正在居住的房子,你可以调整屋内所有的软装,或者重新布置线路,但是绝不可以破坏承重墙,破坏了作为房子在任何时候都应具备安全居住的基本功能。因此,大刀阔斧的硬干和推倒重来并不是重构,而是重写,产生的后果是难以预料的。
作者在第一章的最后还向我们阐述了“重构”和“性能优化”之间的本质区别。虽然两者都需要修改代码,并且都不会改变程序的整体功能,但重构是为了让代码更容易理解,更易于修改。这可能会使代码运行得更快,也可能会使代码运行得更慢。而在性能优化时,我们只关心让代码运行得更快,最终得到的代码有可能是更难理解和维护的。
为何重构(Why)
重构可以提高已有代码质量。
何时重构(When)
一言以蔽之,“重构”是在开发过程中实时的、持续的成长改进过程,应该贯穿于日常工作中,而不应该专门拿一段时间来做重构;同时,不是所有的开发阶段都一定要重构。
至于重构的具体时机,作者认为应该在添加功能、修复功能、审查代码时就可以着手进行重构了。我认为,所谓重构的时机并非是机械性的,而应该是机动性的。换句话说就是,不必执着于历史代码中的部分坏味道,因为有时它并非是当前开发阶段中紧急且重要的关键代码。当你修改bug或者添加新需求时涉及到那些代码区域,而你隐隐觉的气味不对时,就是到了该对这段代码进行重构的时候了,我认为这是一种可以称之为“机会主义重构”的策略。
从另一种层面来说,重构过程本身就是个人技术能力的成长写照,每次重构都是对软件设计理解的一次微调和所学技能的演练,我们既要允许自己产生这种味道(因为这是不可避免的),又要监督自己尽可能按照良好的设计模式和编码习惯小心处理这些代码。
如何重构(How)
重构的关键在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成。而每一步是如何实现的,作者在第四章“代码的坏味道”中介绍了 22 种代码的坏味道以及相关的重构手法,这些都是对类、接口、函数、字段等级别代码的重构手段。
这一章的内容非常贴近我们实际的开发情况,很多不合理的代码经常在我的开发中出现。比如重复代码、过长的函数、魔法数字、过长参数列、霰弹式修改等,因此出现bug调试时,总是需要花费很长时间进行梳理才能找到原因。
这让我想到了部门最近正在进行的开源技术平台Nuget包更新、整体联调任务。领导提供了一个公共的.props文件并放到了项目根目录下,里面包含了所需要的NuGet 包引用,这样,每个解决方案只需要引用这个文件,就可以实现旧Nuget包的替换,这完全体现了重构的核心理念:通过将相同的配置提取到单一位置,统一进行项目配置和依赖版本管理,解决了“霰弹式修改”的核心痛点(一个变化需要分散在多个地方进行),同时只需修改一个文件就可以实现整体项目的版本统一和升级,降低了修改成本。
另一方面,在本书中,作者强调将重构分为多次小的阶段,小的方法和多次再编译测试。虽然《重构》强调的是在单元测试下的微观小步修改,但这种“小步原则”的背后实际上是“快速反馈、频繁验证、风险可控”的核心思路,这在我们的整体联调中也有体现:根据解决方案的生成依赖顺序,依次对文件进行编译测试。虽然需要编译的次数多,但从解决报错定位问题的效率来看,实践证明可以在短时间内实现项目代码升级。
这本书不是一本单纯的工具书,对我来说更像是一种工程思想的指导书。最重要的是,重构不是一场梭哈的技术豪赌。一名值得信赖的工程师,需要具备敏锐的“味道”嗅觉,在单元测试的保护下,遵循“小步快跑”的智慧,进行一次或几次微重构,让每一步都易于理解、易于验证。作为新手,可以从“重命名变量l”或“提取函数”这样一步一个小脚印开始,重构的精髓不在于追求能编写出完美的代码,而在于培养一种持续的、让代码比来时更健康的专业习惯,让每个人都可以成长为“重构”代码的实践者。