🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
过去10年中,重构领域最大的变化可能就是出现了一批支持自动化重构的工具。如果我想给一个Java的方法改名,在IntelliJ IDEA或者Eclipse这样的开发环境中,我只需要从菜单里点选对应的选项,工具会帮我完成整个重构过程,而且我通常都可以相信,工具完成的重构是可靠的,所以用不着运行测试套件。 第一个自动化重构工具是Smalltalk的Refactoring Browser,由John Brandt和Don Roberts开发。在21世纪初,Java世界的自动化重构工具如雨后春笋般涌现。在JetBrains的IntelliJ IDEA集成开发环境(IDE)中,自动化重构是最亮眼的特性之一。IBM也紧随其后,在VisualAge的Java版中也提供了重构工具。VisualAge的影响力有限,不过其中很多能力后来被Eclipse继承,包括对重构的支持。 重构也进入了C#世界,起初是通过JetBrains的Resharper,这是一个Visual Studio插件。后来Visual Studio团队直接在IDE里提供了一些重构能力。 如今的编辑器和开发工具中常能找到一些对重构的支持,不过真实的重构能力各有高低。重构能力的差异既有工具的原因,也受限于不同语言对自动化重构的支持程度。在这里,我不打算分析各种工具的能力,不过谈谈重构工具背后的原则还是有点儿意思的。 一种粗糙的自动化重构方式是文本操作,比如用查找/替换的方式给函数改名,或者完成提炼变量(119)所需的简单结构调整。这种方法太粗糙了,做完之后必须重新运行测试,否则不能信任。但这可以是一个便捷的起步。在用Emacs编程时,没有那些更完善的重构支持,我也会用类似的文本操作宏来加速重构。 要支持体面的重构,工具只操作代码文本是不行的,必须操作代码的语法树,这样才能更可靠地保持代码行为。所以,今天的大多数重构功能都依附于强大的IDE,因为这些IDE原本就在语法树上实现了代码导航、静态检查等功能,自然也可以用于重构。不仅能处理文本,还能处理语法树,这是IDE相比于文本编辑器更先进的地方。 重构工具不仅需要理解和修改语法树,还要知道如何把修改后的代码写回编辑器视图。总而言之,实现一个体面的自动化重构手法,是一个很有挑战的编程任务。尽管我一直开心地使用重构工具,对它们背后的实现却知之甚少。 在静态类型语言中,很多重构手法会更加安全。假设我想做一次简单的函数改名(124):在`Salesman`类和`Server`类中都有一个叫作`addClient`的函数,当然两者各有其用途。我想对`Salesman`中的`addClient`函数改名,`Server`类中的函数则保持不变。如果不是静态类型,工具很难识别调用`addClient`的地方到底是在使用哪个类的函数。Smalltalk的Refactoring Browser会列出所有调用点,我需要手工决定修改哪些调用点。这个重构是不安全的,我必须重新运行所有测试。这样的工具仍然有用,但在Java中的函数改名(124)重构则可以是完全安全、完全自动的,因为在静态类型的帮助下,工具可以识别函数所属的类,所以它只会修改应该修改的那些函数调用点,对此我可以完全放心。 一些重构工具走得更远。如果我给一个变量改名,工具会提醒我修改使用了旧名字的注释。如果我使用提炼函数(106),工具会找出与新函数体重复的代码片段,建议代之以对新函数的调用。在编程时可以使用如此强大的重构功能,这就是为什么我们要使用一个体面的IDE,而不是固执于熟悉的文本编辑器。我个人很喜欢用Emacs,但在使用Java时,我更愿意用IntelliJ IDEA或者Eclipse,很大程度上就是为了获得重构支持。 尽管这些强大的重构工具有着魔法般的能力,可以安全地重构代码,但还是会有闪失出现。通过反射进行的调用(例如Java中的`Method.invoke`)会迷惑不够成熟的重构工具,但比较成熟的工具则可以很好地应对。所以,即便是最安全的重构,也应该经常运行测试套件,以确保没有什么东西在不经意间被破坏。我经常会间杂进行自动重构和手动重构,所以运行测试的频度是足够的。 能借助语法树来分析和重构程序代码,这是IDE与普通文本编辑器相比具有的一大优势。但很多程序员又喜欢用得顺手的文本编辑器的灵活性,希望鱼与熊掌兼得。语言服务器(Language Server)是一种正在引起关注的新技术:用软件生成语法树,给文本编辑器提供API。语言服务器可以支持多种文本编辑器,并且为强大的代码分析和重构操作提供了命令。