由于AI大模型为重构工具注入了“智力”,使得过去的自动重构升级为智能重构,也让重构的操作体验,从过去的菜单操作、鼠标操作与快捷键操作(即所谓的GUI),变革为以自然语言为载体的聊天式操作(即所谓的LUI)。
经过影片出租店的完整演练,对这样一个如麻雀般完整而小的遗留项目展开重构,使得我们对重构建立了一个整体的印象,也有利于我们将前面介绍的各种重构知识串联起来,现在,有必要对整个重构做一次复盘。为了帮助大家更好地理解重构,我认为可以从道、法、术、器这四个层次做一番总结。

图片
道是万物变迁循环中亘古不变的规律,是自然环境、事物的自然规律和发展方向。道以明向,它决定了重构的方向,也决定了在软件研发过程中,什么样的活动才可以称之为是重构。我在第3章给出过重构的定义:在不增加任何新功能的情况下,通过运用一系列可控的改进手段对既有代码做出优化,使其变得更容易理解,更容易复用,更容易扩展。这一定义圈定了重构的范围,主要针对代码层面,也可以具体称之为“代码重构”。
自有重构以来,也陆续有人创造更多的概念,如数据库重构、网站重构以及架构重构。它们与代码重构虽然不同,却遵循相同的道,即在不增加任何新功能且不破坏现有功能的前提下,对目标进行优化和改进。违反了这个“道”,就不能说是重构,又或者说没有达成目标的重构。例如:增加了新功能的任何操作,都不能称之为重构;破坏了现有功能的重构,就不能称之为是合格的重构;如果重构之后,目标没有得到任何优化和改善,则不能称之为是好的重构。对代码如此,对数据库、网站和架构,都是如此,只是优化和改进的目标不同罢了。
本专栏讨论的重构内容都属于代码重构,因此它的优化和改进目标就是让代码变得“更容易理解,更容易复用,更容易扩展”,即提升代码的可读性、可复用性与可扩展性。
法是在探求“道”的过程中经过实践思考、归纳总结出的规则体系和方法原则。法以立本,是实现重构目标的规则和方法。Martin Fowler在《重构》一书中总结的重构手法(不含重构手法的具体操作步骤)是“法”的一部分;我在第4章总结的重构三要素,也可以认为是“法”的一部分;如果重构的代码使用了如Java、C#这样的面向对象语言,则基本的面向对象设计原则和设计模式,也可以称之为是“法”的一部分;由于重构需要单元测试做保护,为单元测试规定的FIRST原则也可以认为是“法”的一部分。
通过前面各章对案例实践的讲解,可以看出这些“法”是正确进行重构的基础,也是对具体操作的指导。例如,第5章和第6章先后介绍的迪米特法则与信息专家模式,很好地指导了类的职责分配,从而决定采用提取方法和移动方法等重构手法;又例如在第10章提到的“关注点分离”原则,它指导开发人员学会分辨关注点,将其分离为不同的职责,并采用提取方法、提取委派等重构手法;再例如第23章提到的“差异式编程”,它决定了继承的设计思路,指导我们在重构时,需要将和PriceCode有关的职责分离到单独的继承体系。
至于具体该如何运用这些“法”,就属于“术”的层次了。术是在规则体系指导下的具体操作技术,只要“道、法”不变,“术”可千变万化。术以立策,如果不通过“术”将抽象的方法和法则转化为实际操作的过程,代码重构就无法落地。除了具体的重构步骤之外,第22章提出的“深度优先”与“广度优先”的结合策略,多种重构手法结合的策略,如内联与提取成员之间的配合,这些内容都是在“法”的指导下实施的具体方法,属于“术”的范围。
器是指有形的物质或有形的工具。器以成事,是实现术和法的物质基础。重构的器主要为IDE(包括与重构相关的插件),也包括重构需要用到的各种框架,如JUnit、AssertJ、Mokito,还包括和代码质量有关的工具,如SonarQube。
“器”同样是变化的,且它的变化更其迅速,随着技术的不断进步,它甚至会不断“吞噬”原本属于术的范围,本该由开发人员具体操作和执行的事情,慢慢被“器”所取代。Martin Fowler在刚刚出版《重构》一书之时,只有一款称为“Refactoring Browser(重构浏览器)”的工具,可以对Smalltalk程序实施一些简单的重构。当时,Martin Fowler总结的许多重构手法(属于“术”的层次)都需要开发人员手动完成,以至于他在书中给出了各种重构手法的具体做法。以最常见的“提取方法”重构为例,书中给出的做法为(参见熊节翻译的《重构》第一版第111页,本文有删减,书中将method翻译为函数):
• 创造一个新函数,根据这个函数的意图来对它命名
• 将提炼出的代码从源函数复制到新建的目标函数中
• 仔细检查提炼出的代码,看看其中是否引用了“作用域限于源函数”的变量
• 检查是否有“仅用于被提炼代码段”的临时变量
• ……
• 将被提炼代码段中需要读取的局部变量,当做参数传给目标函数
• 处理完所有局部变量之后,进行编译
• 在源函数中,将被提炼代码段替换为对目标函数的调用
• 编译,测试
提取方法属于“法”的范围,而具体执行提取方法的以上步骤则属于“术”的范围。之所以一个简单的提取方法都需要定义这么多繁琐的步骤,就是为了执行安全的重构。如果没有工具帮助,就需要开发人员严格地按照这些步骤执行。可是,在本专栏演示“提取方法”重构时,哪有这么麻烦?这是因为这些有规律可循的重构步骤已经被诸如Intellij IDEA提供的重构工具所替代,它帮我们自动完成了对新函数的创建,对提炼代码的复制,对提炼代码段中各种变量的检查,对提炼代码的引用等。
如果说这些重构工具只是“器”对“术”的“侵略”,那么AI大模型的发展则进一步开疆拓土,不仅侵占了“术”的领地,还毫不客气地开始对“法”领地的侵略。
来源 :逸言