1.10 变换
程序员的呐喊
我经常听到有人说Ruby没有自动化的重构工具,所以根本没法用。虽然Ruby将来会具备一些重构功能,但是总会有一些是Java已经自动化,而Ruby没有的。他们说,这是必不可少的功能啊。
对此我表示怀疑。
到底什么是重构?字典里根本就没有这个词。
福勒告诉我们所谓重构,就是通过迭代,将恶心的代码变成优质代码的艺术和科学,是能妆点代码却不会在操作过程中产生破坏的算法,而且正确性都是能证明的。
他的剖析非常出色。他专为Java程序员呈现了一些很不错的技术,有些是早已为大家熟知和习以为常的,而另一些则是全新的技术。
有些“重构”技术是可以自动化的,其中很多也可以运用在Java之外的语言上。看起来似乎福勒和他的朋友们误打误撞,碰对了几乎和OOP一样大的概念。至少他们也是第一个将这个概念包装营销成功的人。无论如何,我们现在都知道这个概念了。谢谢你,福勒和你的朋友们!
《重构》是我见过的编程书里第一本讲述编程中神秘细节的著作。它将整个过程拆解开来,一步一步带着你检验代码中每一个会影响品质的小决策。这些都是原本几乎没人会在意的事情,大家都熟视无睹了,大多数人更关心的是“架构”。重构讨论的是日常工作中代码里的惯例,是真正的代码,而非纸上谈兵。
这是了不起的成就,真的,以前从来没人想到过这些,大家都觉得所谓的风格是程序员自己的选择。《重构》却在字里行间告诉我们哪里错了。很漂亮。
发现重构
我注意到《重构》这本书是在2002年的时候,距离它出版已经好多年了。之前一直没有读是因为出版它的是那帮搞UML的蠢货。我从来都不是它的粉丝。UML在数据库建模上还(可能)有点用,但是在类建模上根本一无是处。我也从来没有在意过布池(Booch)、雅各布森(Jacobsen)等人的书。
《重构》偏偏就和这样一堆烂书挤在一起,每次看到它我都直接扫过,从不多停留一秒。别在烂书上浪费时间!
直到2002年冬,我在一家书店里拿起了它。没什么特别的理由,纯粹出于好奇而已。或许我在其他什么地方听到过“重构”这个词吧。它到底是什么意思?字典里根本查不到嘛。
“分解” 1 倒是字典里有的词。你可以分解数字,也可以分解多项式。这个我懂,但是干嘛要再做一次呢?什么叫“再分解”?(英文中的前缀re-有重新、再次的意思。)
翻开书,局部变量是万恶之源。这或许不是原话,但引入我眼帘的第一段话就在讨论这个。局部变量!?我抓来椅子一屁股坐下来,非常愤怒地往下读。我要看看这个家伙到底是脑子不正常,还是傻瓜一个。
接着一股恐惧袭上心头:他居然说得没错,有理有据。我最自豪的编程习惯(把中间值保存在局部变量里,作为简单的性能优化)显然是个坏习惯,书中明明白白地展示了一点。它解释了我代码里的某些方法为什么会不断膨胀,那就是因为这些方法无法分割,这一点我之前从未想到过。
而这些巨大的方法正是邪恶的温床。那些代码我碰都不想碰。每次不得不去修改它们的时候,黑暗洞穴就会变得愈加邪恶。不管多么不愿意,我还是必须要为之添加新功能,可是那些局部变量已经深入骨髓,像蜘蛛网一样将我困住。
书里解释了为什么它们无法分割,然后提供了斩断它们的利斧——尖锐精准的工具。这些都是很棒的技术,有些甚至还可以自动化,太了不起了。
我越读越快,完全入了迷。
这本书接着告诉我:不要写注释。又是疯话!可是它说得的确有道理。我从此也不再写单行注释,开始编写更直白的函数和参数名。
我掏钱买它回家,反复研读。彻底震惊,太天才了。时至今日我也依然这么觉得,虽然那种震撼没有当天那么巨大。但这本书实乃惊世之作,好似醍醐灌顶,令人茅塞顿开。这种事情可不常见。
突然我心里冒出一个尴尬念头:我怎么没在1998年的时候读这本书呢?这股寒意一下子将我淹没,好像我多年来一直都脱了裤子上班一样。其他人是不是早就读过这本书了?我会不会是唯一不知道的人?
第二天我就四处打探。我假装冷静,好像随口提起一样。你读过《重构》吧?没有。每个人都回答没有,大多数人连听都没听过。我问了20个程序员,只有一个读过这本书。不过他什么书都读,所以没什么好意外的。你问他的读后感,他说:“我读过,那是本好书!”
我顿时松了一口气。既然大多数人都不知道这本书,那我的处境还算安全。我可以好好研读运用,不用担心别人发现我的代码有多烂。它们只是在某几个方面比较糟糕而已,大部分还是经过精心设计的。各种常见的软件工程规范、设计模式、单元测试、版本控制等我都有用到。它只是在有些地方不太漂亮而已,而我现在已经知道怎么改正了。
重构的现状
今天,所有人都知道《重构》这本书,因为很多IDE包含了书中描述的全部自动化重构技术,甚至还有自己的扩展。
尽管一夜爆红,我却怀疑到底有多少程序员真的读过福勒的这本书,恐怕连部分章节都没读过吧。我觉得大多数程序员到今天也不知道,其实还有很多重构技术是无法自动化的,就连Java也无能为力。甚至可以说大部分都是如此。当然这说起来就话长了。
我到今天也不知道他们到底为什么要起“重构”这个名字。我猜可能是比较吸引眼球吧。站在数学的角度上来看,这个词和“分解”勉强有点联系,而重组(reorganization)则太宽泛了,所以“重构”似乎是个好名字。
而有时候能不能取个好名字就能决定一个想法会不会为大众接受。
到今天,重构已经衍生出一整个产业了,它就是一面旗帜。Java IDE的粉丝们都在为之摇旗呐喊。重构工具就好像摆在瓶子里的产品一样。翻开菜单从里面挑一个出来,连地球都能撬起来。
为什么自动化重构在Java阵营里那么流行,在其他语言里却没掀起什么浪花呢?
Java说那是因为只有Java才能将代码变换自动化做到这种程度。
重奏
我经常听到有人说Ruby没有自动化的重构工具,所以根本没法用。虽然Ruby将来会具备一些重构功能,但是总会有一些是Java已经自动化,而Ruby没有的。他们说,这是必不可少的功能啊。
对此我表示怀疑。
现在我已经读过福勒的书了,他说的东西我也懂了。这本书的确是艺术和科学的完美结合,它教你如何通过可以证明的步骤,将糟糕的代码变成优质代码。
但它教给我们的应该不止这些,不是吗?
当然,你得先读一下这本书才知道那是什么。那么,你到底读了没有?从头到尾一字不漏?得了吧,别不承认,你肯定跳读了。
所以事情其实是这样的:要了解代码从烂到好的过程,首先要知道什么叫烂代码。书中首先给出了一些例子,然后解释了为什么这些代码很烂。福勒列出了一些值得警惕的地方,甚至还给它们起了个名字:“代码异味”。从营销的角度来讲是不是很高明?也许吧。不过他们的观点还是正确的。
那么这些代码一开始是怎么变烂的呢?
首先当然是由于过早优化造成的,为了避免重复计算而保存了太多的中间变量。因为害怕方法调用会造成虚幻的负担,而刻意回避编写短小的函数。我们还弄出一大堆类的继承关系,仅仅是为了想象中可能存在的复用,为了避免分配容器对象而弄出一个巨大的参数列表。滥用null,把它当成具有语义的符号。放任简单的布尔逻辑表达式变成错综复杂、无法阅读的浆糊。不用访问方法来封装数据和数据结构。还有其他很多乱七八糟的问题。
正是因为各种各样的小错误累积起来造成了代码品质下降。这本书将它们分门别类,加以命名,并归类成严重错误。
如此一来,我们是不是就不会再犯错了呢?大概读过这本书的人就不会了吧。亡羊补牢,读晚了总比没读过好。至少读过这本书后,你会了解烂代码是什么样子的,也会知道它是怎么沦落到那个样子的,你也会学到避免它的办法。
那么自动化重构工具是什么时候开始受到关注的?这本书一开始关心的是设计,以及修复代码品质的工具。然而现在的焦点却落到了修复上,特别是修复技术里可以自动化的那部分上面。
其实,这其中隐含的意思就是默认烂代码是无法避免的,就算我们了解了它的方方面面,就算我们能一眼发现它也没用。假如你好好读过这本书,就会发现它其实不只是一份列出100多种重构技术的列表,事实上它为我们呈现的是一种思维方式。只要领会了其中的精神,你完全可以发明自己的重构技术,发现新的代码异味。
现在你知道怎么一次就把代码写对了吧。
什么?你不同意?因为代码是活的,需求会不断变化?没错,代码的确需要改。但是重构并非修改代码的全部,它其实只是其中一小部分罢了。此外还有数据建模、架构、设计模式等其他高阶的玩意儿。这还不包括那些敝帚自珍的代码技巧,这些模式很趁手,但是不够通用,没有响亮的名字。修改它们的技巧可不是能在IDE的重构目录下找到的选项。这一点我还是可以肯定的。
重构是微观的。你关心的这个类怎么写,那个方法怎么实现,细节到局部变量,控制流等微设计的决策。
你现在知道的是怎么在那个层面上避免犯错。
当然这是在你读过书,而且是仔细读过以后。另外就是你得有足够的经验才能有被当头棒喝的感觉,否则这些经验教训是无法深入骨髓的。
《重构》让我们变得懒惰了吗?可能吧,特别是假如我们只是快速浏览了一下,只看了讲解怎么做,略过为什么的时候。这会让我们觉得它就是教你怎么将代码从无法避免的腐烂中救回来的办法。就算你真的读了,那些鼓吹自动化重构的言论也会让你忘记这本书真正的主旨。那就是改善你的代码,然后再也不再写这么菜鸟的代码。
那这就是全部了?不是。有时候是不是还是需要重构?没错。有没有什么我还不了解的微妙之处?当然有,单纯地讨论重构太虚了。它和其他现代开发理念是互通的,比如“不要重复自己”,“一次且仅一次”,单元测试等。到时候我还会再提到重构。
不过我今天感兴趣的只是为什么Java程序员认为点几个按钮就能“编程”的功能那么重要,重要到其他语言都不愿考虑。即使这门语言在世界范围内也在迅速得到认可,生产力的提升相较于Java对C++只会多不会少,而且还没有Perl和Python那些前辈的缺点和难用的地方。
我的意思是,这些人根本连试一下Ruby都不愿意?天哪,点按钮一定……非常好玩。
我只得闭上眼睛,努力去想象那种力量……
点按钮的生产力
啊哈,自动化重构。只要点点鼠标就能看到成果;点选菜单就能编程;选择自动攻击,就能撬动地球;不费吹灰之力就能移山。是不是感觉自己像超人?编程似乎简单得像玩电子游戏一样。
这种感觉就像操作巨型机械:约翰迪尔、小松公司、卡特彼勒——那些拥有世界上最大轮胎的黄色机械怪物,我们小时候每次看到都会惊叹不已。司机只要一拉操纵杆,就能铲起一大堆土。原本需要上百人劳动一整天的工作,只在弹指之间就完成了。一台完全在你控制之下的黄色巨兽。
这才叫生产力。假如我的工作是搬运泥土,那我表示同意:没有巨型机械、拖拉机、土方车、大吊车或者挖土机的话,工作起来是完全没有效率可言的。这是我重构地球表面的私人工具。我是绝对不会让你染指这些几百吨的庞然大物的。
卡特彼勒(毛毛虫),对机械巨人来说这真是个奇怪的名字。是不是?其实这个名字来自一种小型的肢节小虫。这种小虫每一节都差不多,每段都有两条一样的小腿,所有的腿以波浪的方式推动小虫向前。你看看,光是在泥泞中爬行就需要进行那么多计算!
好了,现在我懂了。毛毛虫就好像长长的类似机器的昆虫,而巨型机械就是巨大的类似昆虫的机器。开始明白其中的联系了吧。
自动化代码重构工具很适合对付卡特彼勒那样的代码。你面对的是一大堆实体——对象、方法、名称,以及任何有迹可循的东西。它们几乎一模一样。你要以一致的方式修改它们,就像毛毛虫爬行一样,同时移动所有的腿。
那我们的代码是怎么变成那样的呢?因为写得烂。这时重构就能救命。再优秀的设计也会出纰漏,但我们仍然可以补救,反正有自动化的奴仆来帮我们修复这些小问题。它们不知疲倦,我们只要点个按钮就行了。
既然如此,谁能离得开自动化重构工具?还有谁能协调Java那些数以百计的小腿,让它们像毛毛虫一样统一行动呢?
让我来告诉你答案:Ruby是蝴蝶。