重构:改善既有代码的设计——前言

重构:改善既有代码的设计

Refactoring-improving the Design of existing Code
[美]Martin Fowler 著

  • 软件开发的不朽经典
  • 生动阐述重构原理和具体做法
  • 普通程序员进阶到编程高手必须修炼的秘笈

前言

从前,有位资讯顾问造访客户调研其开发项目。系统核心是个类继承体系,顾问看了开发人员所写的一些代码。他发现整个体系相当凌乱,上层超类对于系统的运作做了一些假设,下层子类实现这些假设。但是这些假设并不适合所有子类,导致覆写(override)工作非常繁重。只要在超类做点修改。就可以减少许多覆写工作。在另一些地方,超类的某些意图并未被良好理解,因此其中某些行为在子类内重复出现。还有一些地方,好几个子类做相同的事情,其实可以把它们搬到继承体系的上层去做。

这位顾问于是建议项目经理看看这些代码,把它们整理一下,但是经理并不热衷于此,毕竟程序看上去还可以运行,而且项目面临很大的进度压力。于是经理说,晚些时候再抽时间做这些整理工作。

顾问也把他的想法告诉了在这个继承体系上工作的程序员,告诉他们可能发生的事情。程序员都很敏锐,马上就看出问题的严重性。他们知道这并不全是他们的错,有时候的确需要借助外力才能发现问题。程序员立刻用了一两天的时间整理好这个继承体系,并删掉了其中一半的代码,功能毫发无损。他们对此十分满意,而且发现在继承体系中加入新的类或使用系统中的其他类都更快、更容易了。

项目经理并不高兴。进度排得很紧,有许多工作要做。系统必须在几个月之后发布,而这些程序员却白白耗费了两天时间,干的工作与要交付的多数功能毫无关系。原先的代码运行起来还算正常,他们的新设计看来有点过于追求完美。项目要交付给客户的,是可以有效运行的代码,不是用以取悦学究的完美东西。顾问接下来又建议应该在系统的其他核心部分进行这样的整理工作,这会使整个项目停顿至二个星期。所以这些工作只是为了让代码看起来更漂亮,并不能给系统添加任何新功能。

你对这个故事有什么感想?你认为这个顾问的建议(更进一步这里程序)是对的吗?你会遵循那句古老的工厂谚语吗:“如果它还可以运行,就不要动它。”

我必须承认自己有某些偏见,因为我就是那个顾问。六个月之后这个项目宣告失败,很大的原因是代码太复杂,无法调试,也无法获得可被接受的性能。

后来,项目重新启动,几乎从头开始编写整个系统,Kent Beek受邀做了顾问。他做了几件迥异以往的事,其中最重要的一件就是坚持以持续不断地重构行为来整理代码。这个项目的成功,以及重构在这个成功项目中扮演的角色,启发了我写这本书,如此一来我就能够把Kent和其他一些人已经学会的“以重构方式改进软件质量”的知识,传播给所有读者。

什么是重构

所谓重构(refactoring)是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。重构是一种经千锤百炼形成的有条不许的程序整理的方法,可以最大限度地减少整理过程中引入错误的几率。本质上说,重构就是在代码写好之后改进它的设计。

“在代码写好之后改进它的设计”,这种说法有点奇怪。按照目前对软件开发的理解,我们相信应该先设计而后编码:首先得有一个良好的设计,然后才能开始编码。但是,随着时间流逝,人们不断修改代码,于是根据原先设计所得的系统,整体结构逐渐衰弱。代码质量慢慢沉沦,编码工作从严谨的工程堕落为胡砍乱劈的随性行为。

“重构”正好与此相反。哪怕你手上有一个糟糕的设计,甚至是一堆混乱的代码,你也可以借由重构将它加工成设计良好的代码。重构的每个步骤都很简单,甚至显得有些过于简单:你只需要把某个字段从一个类移到另一个类,把某些代码从一个函数拉出来构成另一个函数,或是在继承体系中把某些代码推上推下就行了。但是,聚沙成塔,这些小小的修改累积起来就可以根本改善设计质量。这和一般常见的“软件会慢慢腐烂”的观点恰恰相反。

通过重构,你可以找出改变的平衡点。你会发现所谓设计不再是一切动作的前提,而是在整个开发过程中逐渐浮现出来。在系统构筑过程中,你可以学习如何强化设计,期间带来的互动可以让一个程序在开发过程中持续保有良好的设计。

本书有什么

本书是一本为专业程序员而写的重构指南。我的目的是告诉你如何以一种可控且高效的方式进行重构。你将学会如何有条不紊地改进程序的结构,而且不会引入错误,这就是正确的重构方式。

按照传统,图书应该以引言开头。尽管我也同意这个原则,但是我发现以概括性的讨论或定义来介绍重构,实在不是件容易的事。所以我决定用一个实例做为开路先锋。第1章展示了一个小程序,其中有些常见的设计缺陷,我把它重构为更合格的面向对象程序。期间我们可以看到重构的过程,以及几个很有用的重构手法。如果你想知道重构到底是怎么回事,这一章不可不读。

第2章讨论重构的一般性原则、定义,以及进行重构的原因,我也大致介绍了重构所存在的一些问题。第3章由Kent Beck介绍如何嗅出代码中的“坏味道”,以及如何运用重构清除这些坏味道。测试在重构中扮演着非常重要的角色,第4章介绍如何运用一个简单而且开源的Java测试框架,在代码中构筑测试环境。

本书的核心部分——重构列表——从第5章延伸至第12章。它不能说是一份全面地列表,只是一个起步,其中包括迄今为止我在工作中整理下来的所有重构手法。每当我想做点什么——例如Replace Conditional with Polymorphism——的时候,这份列表就会提醒我如何一步一步安全前进。我希望这是值得你日后一再回顾的部分。

本书介绍了其他人的许多研究成果,最后几章就是由他们之中的几位所客串写就的。Bill Opdyke在第13章记述他将重构记述应用于商业开发过程中遇到的一些问题。Don Roberts和John Brant在第14章展望重构记述的未来——自动化工具。我把最后一张(第15章)留给重构技术的顶尖大师Kent Beck来压轴。

在Java中运用重构

本书范例全部使用Java撰写。重构当然也可以在其他语言中实现,而且我也希望这本书能够给其他语言使用者带来帮助。但我觉得我最好在本书中只使用Java,因为那是我最熟悉的语言。我会不时写下一些提示,告诉读者如何在其他语言中进行重构,不过我真心希望看到其他人在本书基础上针对其他语言写出更多重构方面的书籍。

为了很好地与读者交流我的想法,我没有使用Java语言中特别复杂的部分。所以我避免使用内嵌类、反射机制、线程以及很多强大的Java特性。这是因为我希望尽可能清楚地展现重构的核心。

我应该提醒你,这些重构手法并不针对并发或分布式编程。那些主题会引出更多的考虑,本书并未涉及。

谁该阅读本书

本书的目标读者是专业程序员,也就是那些以编写软件为主的人。书中的示例和讨论,涉及大量需要详细阅读和理解的代码。这些例子都以Java写成。之所以选择Java,因为它是一种应用范围愈来愈广的语言,而且任何具备C语言背景的人都可以轻易理解它。Java是一种面向对象语言,而面向对象机制对于重构有很大帮助。

尽管关注对象是代码,但重构对于系统设计也有巨大影响。资深设计师和架构师也很有必要了解重构原理,并在自己的项目中运行重构记述。最好是由老资格、经验丰富的开发人员来引入重构技术,因为这样的人最能够透彻理解重构背后的原理,并根据情况以调整,使之使用于特定工作领域。如果你使用的不是Java,这一点尤其重要,因为你必须把我给出的范例以其他语言改写。

下面我要告诉你,如何能够在不通读全书的情况下充分用好它。

  • 如果你想知道重构是什么,请阅读第1章,其中示例会让你清楚重构的过程。
  • 如果你想知道为什么应该重构,请阅读前两章。它们告诉你重构是什么以及为什么应该重构。
  • 如果你想知道该在什么地方重构,请阅读第3章。它会告诉你一些代码特征,这些特征指出“这里需要重构”。
  • 如果你想着手进行重构,请完整阅读前四章,然后选择性地阅读重构列表。一开始只需概略浏览列表,看看起其中有些什么,不必理解所有细节。一旦真正需要实施某个准则,再详细阅读它,从中获取帮助。列表部分是供查阅的参考性内容,你不必一次就把它全部读完。此外你还应该读一读列表之后其他作者的“客串章节”,特别是第15章。
发表评论

电子邮件地址不会被公开。