任何傻瓜都能写计算机能理解的代码,优秀的程序员能够编写人能理解的代码。—— Martin Fowler
这些日子里,由于项目的缘故,我又双叕开始学着造轮子了。故事的开始是代码的不规范堆砌,导致软件大楼摇摇欲坠;故事的终点是,重新唤醒程序员对匠艺的追求。而故事的中间部分,则是我们所要关注的内容:代码坏味道(code smell)、包依赖合理性,应对的方案则是代码重构,目标则是 Clean Code,即易于阅读的代码。而它们(代码坏味道、重构方式等)都已经被归纳为模式。
而所谓的模式指的是:模式,在物体或事件上,产生的一种规律变化与自我重复的样式之过程。既然,它们是可重复的,那么也存在一定的自动化可能性。自动化解决重复性问题,是程序员的使命之一。
程序员的另外一个使命是造轮子。不求造出完美的轮子,只要是可工作的,并且不断改进地轮子,都终将是一个好轮子。
扯完了,回到正题。
好吧,这个小节的内容是额外多出来的。至少在当前,我还没有想到好的方式,来通过自动化的方式,来识别出一个架构是否出现问题了。如果有的话,那么一定是:
好吧,我编不下去了,因为大部分用肉眼就能看出来的了。而若是自动化,则能很大程度上降低人力成本。
而应对于架构拆分的模式,应对的方式无非就是: 拆与合 。
一个良好的软件团队,必然拥有强大的工程能力。正是这些工程能力,让每个新加入的成员,能在约束的情况下快速上手。所以,我们可以在不阅读代码的时候,了解到详细的情况。
commitizen
这显然不是一件容易的事,它涉及到一系列的整体性方案。
对于遗留代码来说,我们所做的主要工作是: 解除依赖性 。而要打破依赖并不是一件容易的事,它所涉及的是一层层的函数调用,以及其背后的复杂业务逻辑。只需要构建出目标语言的 AST(抽象语法树),我们便能分析出项目中的各个包间的依赖关系。
然而,即便我们知道了两个类、包、服务之间出现循环引用,要自动化打破这些依赖也不是一句容易的事,因为处理的规则太多了。诸如于:
所以,可视化依赖关系,在当前而言,是一个不错的选择。我们所要做的便是:
只是呢,就我当前的经验而言,还无法做到这部分的完全自动化。
如果代码有味道的话,那么你所能闻到的一定是坏的那种,因为『久居兰室不闻其香』,对于稍有经验的开发人员来说,寻找代码的坏味道,是一件简单的事情——从代码里挑刺嘛。当然了,我不得不再说一下,挑刺很容易,但是难的是给出 改正手段 ——即如何重构。
步骤是:
而对于这些坏味道来说,有的易于识别,有的没必要识别。
对应的也可以按分组来判断各自的需要。
分组 | 坏味道 |
---|---|
Bloaters | Long Method, Large Class, Primitive Obsession, Long Parameter List, Data Clumps |
Object-Orientation abusers | Switch Statements, Temporary Field, Refused Bequest, Alternative Classes with Different Interfaces |
Change Preventers | Divergent Change, Shotgun Surgery, Parallel InheritanceHierarchies |
Dispensables | Lazy class, Data class, Duplicate Code, Dead Code, Speculative Generality |
Couplers | Feature Envy, Inappropriate Intimacy, Message Chains, Middle Man |
除此,还有一些不在列表里的坏味道,比如:
所以,如果能对坏味道进行自动化,就能减少了多人力工作。
当然了,使用 SonarQube、CheckStyle、FindBugs 和 PMD 等也能找到对应的 Code Smells,只是呢,大部分情况下,我们遇到的问题要比这复杂得多。而实践证明,那些“机智”(鸡贼)的开发人员,已经有各种办法绕过这些措施。
自动化的测试代码编写,依赖于大量的先验知识。如果我们计划于生成某个函数的测试,那么我们首先必然需要调用这个函数,才能返回预期的结果。而测试的完整性,实质上是依赖于边界条件来构建的。如一个对字符串处理的函数的测试,我们需要:
当然了,这种自动化的测试,多数时候都不是那么可靠的。不过,是一个非常不错的 Monkey 测试。
为了完成这部分的测试生成,我们需要:
每个都不是一件容易的事,都需要大量的经验。当然了,要编写这样的一个应用进行自动化更不容易。
开始之前,让我们先统一一下语言:『什么是重构』
重构(refactoring)是指在不改变外在行为的前提下,对代码做出修改,以改进程序的内部结构。
所以,只要改变了现有的行为,都不能称之为重构。按习惯,我们将代码重构分为了三类:
对于手动重构来说,我们首先依赖于先前识别的 code smell(代码坏问题)。而在重构一书中,给出了每一种坏问题的对应修改方法。如应对于 被拒绝的遗赠(Refused Bequest) ,推荐了三种对应的修改方法:
其中的 3 和 4 在《重构1》中合称为:Replace Inheritance with Delegation(以委托取代继承)。
基于此,我们就可以拥有一套完整的端到端重构工具集。
有没有这样的工具呢?
有。 GitHub:https://github.com/phodal/coca
Coca 是一个使用 Go 编写的 Java 语言自动化重构工具。架构方面能生成对应的依赖树,重构方面支持批量重命名方法、寻找未使用的类、删除未使用的 import、移动类到指定目录等。即将支持本文中的剩下功能。