重构的核心价值:以功能为导向的代码优化

本文深入探讨了代码重构的本质,强调重构应该始终以产品功能为导向。通过生动的比喻和实际案例,作者阐述了如何通过重构提升代码质量,同时避免过度工程化,实现开发效率与代码可维护性的平衡。

重构的核心价值:以功能为导向的代码优化

重构的本质

在清理代码时,你始终是在为产品服务。重构本质上是一个组织过程(不是指“与业务相关”的定义,而是指“将事物整理有序”的定义)。也就是说,你整理代码是为了能够完成某些事情。

当你开始仅仅为了重构而重构时,重构就会背上坏名声。人们会认为你在浪费时间,你会失去信誉,你的经理或同事会阻止你继续工作。

重构的误区

所谓“仅仅为了重构而重构”,指的是看着一段与你实际工作无关的代码,说“我不喜欢这个设计方式”,然后在不影响系统功能的情况下调整设计部分。这就像房子着火时还在给草坪浇水。如果你的代码库像我见过的大多数代码库一样,“你的房子着火了”可能是一个恰当的比喻。

即使情况没有那么糟糕,关键是你正在关注不需要关注的东西。你可能觉得自己在重组代码方面做得很好,也许确实如此,但给草坪浇水的目的是让房子前面有一片漂亮的草坪。如果你的重构与系统的当前产品或功能目标无关,那么你实际上只是在重新整理一些没有人使用、参与或关心的东西。

正确的重构方法

那么你应该怎么做?通常,你应该选择一个想要实现的功能,然后找出可以通过重构来使该功能更容易实现的部分。或者找到代码中经常被修改的区域,并在该区域进行一些重组。

这样人们会欣赏你的工作。这不仅是因为他们会欣赏,更重要的是因为你正在做有效的事情。但对已完成工作的欣赏——或者至少某种形式的礼貌认可——可以帮助鼓励你继续前进,可以让你知道其他人开始关心你的工作,并有望帮助在整个公司传播良好的开发实践。

重构的边界

我通常会在代码周围设置一些边界,比如“我不会为了完成这个功能而重构项目之外的任何东西”,或者“我不会在能够发布这个功能之前等待编程语言本身的更改”。但在我的边界内,我尽力做好工作。我尽量将边界设置得尽可能宽,但不会陷入无法实际开发功能的情况。

通常这既是时间边界,也是“代码库范围”的边界(比如,距离我的代码库有多远)——时间部分通常是最重要的,比如“我不会为了开发一个两天的功能而进行一个三个月的项目”。但即使如此,我仍然会在重构上花费时间,特别是当我第一次在代码库中这样做,而且这是一个新事物,整个事情非常混乱时。

重构的时间效益

这引出了另一点——尽管你可能认为重构然后开发功能会花费更多时间,但根据我的经验,通常总体花费的时间更少或相同。这里的“总体”包括你花费在调试、回滚发布、发送错误修复、为复杂系统编写测试等方面的所有时间。

在不重构的情况下在复杂系统中编写功能可能看起来更快,有时确实如此,但大多数情况下,如果你在开始添加新功能之前先将系统整理好,总体花费的时间会更少。这不仅仅是理论上的——我已经多次证明这一点。实际上,当我们这样做时,我的团队比那些使用更好工具处理较新代码库的团队更快完成项目。

重构的完成标准

我用来决定何时“完成”重构特定代码的另一点是,我认为其他人将能够清楚地看到我设计的模式,并从此以后按照该模式维护代码。有时我必须编写一小段文档来描述系统的预期设计,以便人们遵循它,但总的来说,我的理论(这个真的只是一个理论——我还没有足够的证据)是,如果我设计一段代码足够好,它不应该需要一段文档来描述它应该如何设计。仅仅通过阅读代码就应该能够看出它是如何设计的,并且在该设计中添加新功能的方式应该非常明显,以至于没有人会以其他方式这样做。

显然,完美实现这个目标是不可能的,但这是软件设计中的一个普遍真理:没有完美的设计,只有更好的设计。

避免过度工程化

所以这是你知道自己在“过度工程化”或在弄清楚如何重构某物上花费太多时间的另一种方式——你试图让它“完美”。它不会“完美”,因为没有“完美”。只有“对其目的做得很好”。也就是说,如果不理解代码设计的目的,你甚至无法真正判断设计是否良好。一个设计对一个目的很好,另一个设计对另一个目的很好。是的,有通用库,但即使那也是一个目的。最好的通用库是通过实际代码库的实验设计的,你可以验证它们很好地服务于特定目的。

当你重构时,想法是将设计从当前不适合目的的设计更改为适应该代码当前目的的设计。这不是关于重构的全部知识,但这是一个很好的基本原则。

总结

简而言之,重构是你为了实现生产而进行的一个组织过程。如果你在重构时没有朝着生产方向前进,你会遇到很多不同类型的麻烦。我甚至无法告诉你所有会出错的事情,但它们会发生。另一方面,如果你只是试图生产一个系统而从不重新组织它,你会陷入如此混乱的境地,以至于生产变得困难或不可能。

所以这两件事都必须完成——你必须生产一个产品,并且你必须以能够快速、可靠、简单且良好地生产产品的方式来组织系统。如果你忽略了组织,你将无法得到你想要的产品,如果你忽略了生产,那么首先进行重构就完全没有理由。

是的,给草坪浇水很好,但让我们先灭火吧。

-Max

评论

steven gordon 说: 2017年5月4日上午9:32 我基本同意。 然而,我曾有过糟糕的经历,有开发人员基于单一用途将代码重构为设计模式。作者写道:“我用来决定何时‘完成’重构特定代码的另一点是,我认为其他人将能够清楚地看到我设计的模式,并从此以后按照该模式维护代码。” 我倾向于在代码模式的第一个潜在实例上更早停止,以便让代码库在其他地方出现类似代码,然后我可以重构到一个可以被所有这些地方利用的模式。重构涌现的代码创建的设计模式更容易理解(给定多次调用)并且可证明可重用。 这仍然支持主要前提。重构到可重用设计模式是由类似代码的第N次新出现触发的(N = 3?)。

David Coleman 说: 2017年7月1日上午7:26 我喜欢N=3。在那之前,你没有足够的知识来可靠地构建可重用的东西(除非你正在构建广泛使用的API)。

Max Kanat-Alexander 说: 2017年7月26日晚上9:40 N = 3不可靠。修改系统的开发人员不知道有三个实例。你并不总是修改系统的唯一开发人员,所以你有数据没有帮助。使用N = 3意味着你实际上在鼓励代码重复。我想我在http://www.codesimplicity.com/post/two-is-too-many/的评论中详细讨论过这一点。 -Max

Steven Gordon 说: 2017年8月4日上午8:35 代码库应该由整个团队共享。只允许N个团队成员熟悉代码的孤岛会将你的巴士数减少到N。 问你的队友“有没有人已经写过类似我即将编写的代码?”会带来更高的生产力和更少的冗余。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计