阅读 50 行代码还是使用提取方法重构? - Derek Comartin 的见解
在软件开发领域,重构是必不可少的。 提取方法重构(Extract Method Refactoring)是最常见、最受鼓励的技术之一--将大段代码分解成更小的、可重用的方法,以提高可读性和重用性。 虽然这在理论上听起来很理想,但 Derek Comartin 在他的视频"我宁愿读 50 行,也不愿意提取方法重构"中提出了全新的批判性观点。
本文将通过 Derek 对提取方法重构的分析,为您提供真实的背景和实用的建议。我们将按照他的精确推理和代码结构,向我们介绍何时以及如何应用提取重构,以及其实现细节和潜在缺点。
示例:聊天系统中的用户注册
在视频的开头,Derek 介绍了一个例子:聊天系统的注册功能。 这是一个紧凑但现实的代码块,约有 50 行,可执行多项任务:
检查用户名是否为空
验证用户名是否已被使用
处理有年龄限制的渠道
保存新用户对象
- 发送带有激活链接的电子邮件
这段代码位于一个函数中,乍一看,它可能是提取方法重构的最佳候选。 但正如德里克警告的那样,不了解影响而盲目重构实际上会降低清晰度。
从重构开始:选择提取方法
Derek 从许多开发人员的工作开始,将代码片段分解成更小的片段。 他展示了如何在大多数集成开发环境或代码编辑器中通过上下文菜单或键盘快捷键选择 "提取方法"。
他摘录了
验证用户名是否为空
existingSignUpNotActivated 用于检查未激活的账户
validateExistingUser 用于处理所有现有用户检查
filterAgeRestrictedChannels 用于处理 18 岁以下用户的频道
- 发送电子邮件以发出欢迎电子邮件
他给每个新函数都起了一个有意义的名字,这也是清洁代码实践中经常提倡的顶级技巧之一。 但是,在阅读这些修改过的版本时,德里克开始指出逻辑上的漏洞--不是功能上的,而是可读性和控制流上的。
问题 1:隐藏的实现细节
Derek 最先发现的一个问题是,实现细节现在被隐藏在提取的方法后面。
例如,validateUsername 和 validateExistingUser 方法实际上会抛出异常。 但是,作为一名阅读重构代码的开发人员,除非您能访问它们的内部结构,否则您将一无所知。
这种重构可能会隐藏控制逻辑,导致错误或遗漏验证。 范围和流程不再明显。 你不仅没有使代码更加清晰,反而创建了一个抽象的迷宫,在这个迷宫中,异常或修改过的变量等副作用在最初编写逻辑的表单中不再可见。
问题 2:定向和链式提取
接下来,Derek 指出了间接问题--当一个提取方法调用另一个提取方法时,等等。 他展示了 validateExistingUser 方法本身是如何由 existingSignUpNotActivated 组成的。
您阅读的不再是简单的自上而下的代码块。 您在方法、文件和类之间跳来跳去,只是为了追踪发生了什么。 虽然编辑器可以帮助引导这一流程,但它会成为读者认知负荷的负担。
在大型系统中,重构跨越多个文件或组件,这就变得更加困难。 突然之间,您的 "简洁代码 "比原来 "杂乱无章 "的 50 行代码更难理解了。
问题 3:本地变量和状态突变
本视频中最重要的课程之一来自对局部变量和状态突变的处理。
Derek 重点介绍了 filterAgeRestrictedChannels 方法。 它不会返回结果,而是直接更改传入的通道列表。 这意味着您要在不同的方法中修改本地状态,除非您仔细检查该方法,否则这种更改是隐藏的。
这就打破了人们的期望,即函数要么是纯粹的操作,要么在改变事物时发出明确的信号。 当您用一个不返回值但会在内部更改值的新方法来替换逻辑时,您就会带来风险和混乱。
Derek 的重构替代方案
那么,德里克究竟是如何重构他的旧代码的呢?
他提出了一种简单得多的方法:
1.保持内嵌的自明逻辑。 对空用户名的初始检查仍保留在主方法中,因为这很容易理解,也不会扰乱代码库。
2.返回结果而不是突变。 现在,filterAgeAppropriateChannels 函数不再改变频道列表,而是返回一个过滤后的列表。这使得数据流更加清晰,并避免了意想不到的副作用。
3.使用简单、可预测的提取方法。 唯一提取的其他方法是 isExistingUserAlreadyActivated,该方法明确返回一个布尔值,且不会抛出异常。 在不隐藏细节的情况下封装逻辑。
4.避免电子邮件发送等内联副作用。 Derek 将电子邮件逻辑保留在原处进行演示,但建议在实际系统中,应通过单独进程或线程中的事件进行处理,而不是直接与用户表单提交绑定。
总的来说,Derek 只使用了两种提取方法,其余的逻辑都是内嵌的,因为这样更易于阅读、推理和控制。
提取方法重构的最后提示
Derek 的视频为我们留下了一些有效使用提取方法重构的实用指南:
使用有意义的名称,准确描述方法的作用。
避免出现状态突变或异常抛出等副作用,除非它们显而易见。
返回值而不是修改输入参数。
不要将逻辑隐藏在多层抽象之后。
- 如果一个方法的原始形式是可读的,就不要为了重构而将其强行转换成多个函数。
有时,最好的抽象就是不抽象--尤其是以牺牲清晰度和范围意识为代价的时候。
结论
Derek Comartin 的方法挑战了重构总能改进代码的观念。 在提取方法重构的情况下,"少 "往往就是 "多"。 不要过度使用选择提取法(Extract Method)来分解逻辑,而是要评估哪些内容会增加价值,哪些内容会让代码更容易理解,哪些内容会隐藏重要细节。
Derek 在他的 视频中通过清晰的示例和对实际代码的直接洞察,向读者展示了有时在一个方法中使用 50 行代码,像故事一样自上而下地阅读,比在代码库中分散使用 10 个较小的方法更好。
如果您曾经为了创建一个新方法而使用键盘快捷键,请记住 Derek 的建议:暂停、评估,确保重构是为读者服务的,而不仅仅是为集成开发环境服务的。

