# 前言
从前,有位咨询顾问参访一个幵发项目。系统核心是个class hierarchies (类阶层体系〉,顾问看了开发人员所写的一些代码。他发现整个体系相当凌乱,上层classes对于classes的运作做了一些假设,下层(继承〕classes实现这些假设。但是这些假设并不适合所有subclasses,导致覆写〔overridden)行为非常繁重。只要在superclass做点修改,就可以减少许多覆写必要。在另一些地方,superclass的某些意图并未被良好理解,因此其中某些行为在subclasses内重复出现。还有一些地方,好几个subclasses做相同的事情,其实可以把它们搬到class hierarchy的上层去做。
这位顾问于是建议项目经理看看这些代码,把它们整理一下,但是经理并不热衷于此,毕竟程序看上去还可以运行,而且项目面临很大的进度压力。于是经理说,晚些时候再抽时间做这些整理工作。
顾问也把他的想法告诉了在这个class hierarchy上工作的程序员,告诉他们可能发生的事情。程序员都很敏锐,马上就看出问题的严重性。他们知道这并不全是他们 的错,有时候的确需要借助外力才能发现问题。程序员立刻用了一两天的时间整理好这个class hierarchy,并删掉了其中一半代码,功能毫发无损。他们对此十分满意, 而且发现系统速度变得更快,更容易加入新classws或使用其他classes。
项目经理并不高兴。进度排得很紧,许多工作要做。系统必须在几个月之后发布,许多功能还等着加进去,这些程序员却白白耗费两天时间,什么活儿都没干。原先的代码运行起来还算正常,他们的新设计显然有点过于「理论」且过于「无瑕」。项目要出货给客户的,是可以有效运行的代码,不是用以取悦学究们的完美东西。顾问接下来又建议应该在系统的其他核心部分进行这样的整理工作,这会使整个项目停顿一至二个星期。所有这些工作只是为了让代码看起来更漂亮,并不能给系统添加任何新功能。
你对这个故事有什么看法?你认为这个顾问的建议(更进一步整理程序〉是对的吗?你会因循那句古老的工程谚语吗:「如果它还可以运行,就不要动它」。
我必须承认我自己有某些偏见,因为我就是那个顾问。六个月之后这个项目宣告失败,很大的原因是代码太复杂,无法除错,也无法获得可被接受的性能。
后来,项目重新启动,几乎从头开始编写整个系统,Kent Beck被请去做了顾问。 他做了几件迥异以往的事,其中最重要的一件就是坚持以持续不断的重构行为来整理代码。这个项目的成功,以及重构(refactoring)在这个成功项目中扮演的角色, 促成了我写这本书的动机,如此一来我就能够把Kent和其他一些人已经学会的「以 重构方式改进软件质量」的知识,传播给所有读者。
### 什么是重构?
所谓重构是这样一个过程:「在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构」。重构是一种有纪律的、经过训练的、有条不紊的程序整理方法,可以将整理过程中不小心引入错误的机率降到最低。本质上说,重构就是「在代码写好之后改进它的设计」。
「在代码写好之后改进它的设计」?这种说法有点奇怪。按照目前对软件幵发的理解,我们相信应该先设计而后编码(coding)。首先得有一个良好的设计,然后才能开始编码。但是,随着时间流逝,人们不断修改代码,于是根据原先设计所得的系统,整体结构逐渐衰弱。代码质量慢慢沉沦,编码工作从严谨的工程堕落为胡砍乱劈的随性行为。
「重构」正好与此相反。哪怕你手上有一个糟糕的设计,甚至是一堆混乱的代码,你也可以借由重构将它加工成设计良好的代码。重构的每个步骤都很简单,甚至简单过了头,你只需要把某个值域(field)从一个class移到另一个class,把某些代码从一个函数(method)拉出来抅成另一个函数,或是在class hierarchy中把某些代码推上推下就行了。但是,聚沙成塔,这些小小的修改累积起来就可以根本改善设计质量。这和一般常见的「软件会慢慢腐烂」的观点恰恰相反。
通过重构(refactoring),你可以找出改变的平衡点。你会发现所谓设计不再是一切动作的前提,而是在整个开发过程中逐渐浮现出来。在系统构筑过程中,你可以 学习如何强化设计;其间带来的互动可以让一个程序在开发过程中持续保有良好的设计。
### 本书有些什么?
本书是一本重构指南(guide to refactoring),为专业程序员而写。我的目的是告诉你如何以一种可控制且高效率的方式进行重构。你将学会这样的重构方式:不引入「臭虫」(错误〉,并且有条不紊地改进程序结构。
按照传统,书籍应该以一个简介开头。尽管我也同意这个原则,但是我发现以概括性的讨论或定义来介绍重构,实在不是件容易的事。所以我决定拿一个实例做为开路先锋。第1章展示一个小程序,其中有些常见的设计缺陷,我把它重构为更合格的面向对象程序。其间我们可以看到重构的过程,以及数个很有用的重构准则。如果你想知道重构到底是怎么回事,这一章不可不读。
第2章涵盖重构的一般性原则、定义,以及进行原因,我也大致介绍了重构所存在的一些问题。第3章由Kent Beck介绍如何嗅出代码中的「坏味道」,以及如何运用重构清除这些坏味道。「测试」在重构中扮演非常重要的角色,第4章介绍如何运用一个简单的(源码开放的〕Java测试框架,在代码中构筑测试环境。
本书的核心部分,重构名录(catalog of refactoring),从第5章延伸至第12章。 这不是一份全面性的名录,只是一个起步,其中包括迄今为止我在工作中整理下来 的所有重构准则。每当我想傲点什么——例如Replace Conditional with Polymorphism——的时候,这份名录就会提醒我如何一步一步安全前进。我希望这是值得你日后一再回顾的部分。
本书介绍了其他人的许多研究成果,最后数章就是由他们之中的几位所客串写就。Bill Opdyke在第13章记述他将重构技术应用于商业开发过程中遇到的一些问题。Don Roberts和John Brant在第14章展望重构技术的未来一自动化工具。我把最 一章(第15章)留给重构技术的顶尖大师,Kent Beck。
### 在Java中运用重构
本书全部以Java撰写实例。重构当然也可以在其他语言中实现,而且我也希望这本书能够给其他语言使用者带来帮助。但我觉得我最好在本书中只使用Java,因为那是我最熟悉的语言。我会不时写下一些提示,告诉读者如何在其他语言中进行重构,不过我真心希望看到其他人在本书基础上针对其他语言写出更多重构方面的书籍。
为了最大程度地帮助读者理解我的想法,我不想使用Java语言中特别复杂的部分。所以我避免使用inner class(内嵌类)、reflection(反射机制)、thread(线程〕以及很多强大的Java特性。这是因为我希望尽可能清楚展现重构的核心。
我应该提醒你,这些重构准则并不针对并发(concurrent)或分布式(distributed)编程。那些主题会引出更多重要的事,超越了本书的关心范围。
### 谁该阅读本书
本书瞄准专业程序员,也就是那些以编写软件为生的人。书中的示例和讨论,涉及大量需要详细阅读和理解的代码。这些例子都以Java完成。之所以选择Java,因为它是一种应用范围愈来愈广的语言,而且任何具备C语言背景的人都可以轻易理解它。Java是一种面向对象语言,而面向对象机制对于重构有很大帮助。
尽管关注对象是代码,重构(refactoring)对于系统设计也有巨大影响。资深设计师(senior designers)和架构规划师(architects)也很有必要了解重构原理,并在自己的项目中运用重构技术。最好是由老资格、经验丰富的开发人员来引入重构技术,因为这样的人最能够良好理解重构背后的原理,并加以调整,使之适用于特定 工作领域。如果你使用Java以外的语言,这一点尤其必要,因为你必须把我给出的范例以其他语言改写。
下面我要告诉你:如何能够在不遍读全书的情况下得到最多知识。
- 如果你想知道重构是什么,请阅读第1章,其中示例会让你清楚重构过程。
- 如果你想知道为什么应该重构,请阅读前两章。它们告诉你「重构是什么」以及「为什么应该重构」。
- 如果你想知道该在什么地方重构,请阅读第3章。它会告诉你一些代码特征,这些特征指出「这里需要重构」。
- 如果你想真正(实际〉进行重构,请完整阅读前四章,然后选择性地阅读重构名录(refactoring catalog)。一开始只需概略浏览名录,看看其中有些什么,不必理解所有细节。一旦真正需要实施某个准则,再详细阅读它,让它来帮助你。名录是一种具备查询价值的章节,你也许并不想一次把它全部读完。此外你还应该读一读名录之后的「客串章节」,特别是第15章。
### 站在前人的肩膀上
就在本书一开始的此刻,我必须说:这本书让我欠了一大笔人情债,欠那些在过去十年中做了大量研究工作并开创重构领域的人一大笔债。这本书原本应该由他们之中的某个人来写,但最后却是由我这个有时间有精力的人捡了便宜。
重构技术的两位最早拥护者是Ward Cunningham和Kent Beck。他们很早就把重构作为开发过程的一个核心成份,并且在自己的开发过程中运用它。尤其需要说明的 是,正因为和Kent的合作,才让我真正看到了重构的重要性,并直接激励了我写这一本书。
Ralph Johnson在University of Illinois,Urbana-Champaign(伊利诺斯大学乌尔班纳分校)领导了一个小组,这个小组因其对对象技术(object technology)的实际贡献而闻名。Ralph很早就是重构技术的拥护者,他的一些学生也一直在研究这个课题。 Bill Opdyke的博士论文是重构研究领域的第一份详细书面成果。John Brant和Don Roberts则早已不满足于写文章了,他们写了一个工具―重构浏览器(Refactoring Browser),对Smalltalk程序实施重构工程。
### 致谢
尽管有这些研究成果帮忙,我还需要很多协助才能写出这本书。首先,并且也是最重要的,给了我巨大的帮助。Kent在底特律(Detroit)和我谈起他正在为Smalltalk Report撰写一篇论文[Beck, hanoi],从此播下本书的第一颗种子。那篇论文不但让我开始注意到重构技术,而且我还从中「偷」了许多想法放到本书第1章。Kent也在其他地方帮助我,想出「代码味道」这个概念的是他,当我遇到各种困难时,鼓励我的人也是他,常常和我一起工作助我完成这本书的,还是他。我常常忍不住这么想:他完全可以自己把这本书写的更好。可惜有时间写书的人是我,所以我也只能希望自己不要做的太差。
写这本书的时候,我希望能把一些专家经验直接与你分享,所以我非常感激那些花时间为本书添加材料的人。Kent Beck, John Brant, William Opdyke, 和Don Roberts编撰或合著了本书部分章节。此外Rich Garzaniti和Ron Jeffries帮我添加了一些有用的补充资料。
在任何像这样的一本书里,作者都会告诉你,技术审阅者提供了巨大的帮助。一如以往,Addison-Wesley的Carter Shanklin和他的优秀团队是一群精明的审阅者。他们是:
- Ken Auer, Rolemodel Software, Inc.
- Joshua Bloch, Sun Microsystems, Java Software
- John Brant, University of Illinois at Urbana-Champaign
- Scott Corley, High Voltage Software, Inc.
- Ward Cunningham, Cunningham & Cunningham, Inc.
- Stéphane Ducasse
- Erich Gamma, Object Technology International, Inc.
- Ron Jeffries
- Ralph Johnson, University of Illinois
- Joshua Kerievsky, Industrial Logic, Inc.
- Doug Lea, SUNY Oswego
- Sander Tichelaar
他们大大提高了本书的可读性和准确性,并且至少去掉了一些任何手稿都可能会有的潜在错误。在此我要特别感谢两个效果显著的建议,这两个建议让我的书看上去耳目一新:Ward和Ron建议我以重构前后效果(包括代码和UML图)并列的方式写第1章,Joshua建议我在重构名录中画出代码梗概(code sketches)。
除了正式审阅小组,还有很多非正式的审阅者。这些人或看过我的手稿,或关注我的网页并留下对我很有帮助的意见。他们是Leif Bennett, Michael Feathers, Michael Finney, Neil Galarneau, Hisham Ghazouli, Tony Gould, John Isner, Brian Marick, Ralf Reissing, John Salt, Mark Swanson, Dave Thomas, 和 Don Wells。我相信肯定还有一些被我遗忘的人,请容我在此向你们道歉,并致上我的谢意。
有一个特别有趣的审阅小组,就是「恶名昭彰」的University of Illinois at Urbana-Champaign读书小组。由于本书反映出他们的众多研究成果,我要特别感谢他们的成就。这个小组成员包括Fredrico "Fred" Balaguer, John Brant, Ian Chai, Brian Foote, Alejandra Garrido, Zhijiang "John" Han, Peter Hatch, Ralph Johnson, Songyu "Raymond" Lu, Dragos-Anton Manolescu, Hiroaki Nakamura, James Overturf, Don Roberts, Chieko Shirai, Les Tyrell, 和 Joe Yoder。
任何好想法都需要在严酷的生产环境中接受检验。我看到重构对于Chrysler Comprehensive Compensation system (C3)系统起了巨大的影响。我要感谢那个团队的所 有成员:Ann Anderson, Ed Anderi, Ralph Beattie, Kent Beck, David Bryant, Bob Coe, Marie DeArment, Margaret Fronczak, Rich Garzaniti, Dennis Gore, Brian Hacker, Chet Hendrickson, Ron Jeffries, Doug Joppie, David Kim, Paul Kowalsky, Debbie Mueller, Tom Murasky, Richard Nutter, Adrian Pantea, Matt Saigeon, Don Thomas, 和 Don Wells。和他们一起工作所获得的第一手数据,巩固了我对重构原理和利益的认识。他们在重构技术上不断进步,极大程度地帮助我看到:一旦重构技术应用于历时多年的大型项目中,可以起怎样的作用。
再—次,我得到了Addison-Wesley 的J. Carter Shanklin和其团队的帮助,包括Krysia Bebick, Susan Cestone, Chuck Dutton, Kristin Erickson, John Fuller, Christopher Guzikowski, Simone Payment, 和 Genevieve Rajewski。与优秀出版商合作是一个令人愉快的经验,他们会提供给作者大量的支援和帮助。
谈到支援,为一本书付出最多的,总是距离作者最近的人。对我来说,那就是我(现在)的妻子Cindy。感谢你,当我埋首工作的时候,你还是一样爱我。当我投入书 中,总会不断想起你。
Martin Fowler
Melrose, Massachusetts
fowler@acm.org
[http://www.martinfowler.com](http://www.martinfowler.com/)
[http://www.refactoring.com](http://www.refactoring.com/)
- 译序 by 侯捷
- 译序 by 熊节
- 序言
- 前言
- 章节一 重构,第一个案例
- 起点
- 重构的第一步
- 分解并重组statement()
- 运用多态(Polymorphism)取代与价格相关的条件逻辑
- 结语
- 章节二 重构原则
- 何谓重构
- 为何重构
- 「重构」助你找到臭虫(bugs)
- 何时重构
- 怎么对经理说?
- 重构的难题
- 重构与设计
- 重构与性能(Performance)
- 重构起源何处?
- 章节三 代码的坏味道
- Duplicated Code(重复的代码)
- Long Method(过长函数)
- Large Class(过大类)
- Long Parameter List(过长参数列)
- Divergent Change(发散式变化)
- Shotgun Surgery(散弹式修改)
- Feature Envy(依恋情结)
- Data Clumps(数据泥团)
- Primitive Obsession(基本型别偏执)
- Switch Statements(switch惊悚现身)
- Parallel Inheritance Hierarchies(平行继承体系)
- Lazy Class(冗赘类)
- Speculative Generality(夸夸其谈未来性)
- Temporary Field(令人迷惑的暂时值域)
- Message Chains(过度耦合的消息链)
- Middle Man(中间转手人)
- Inappropriate Intimacy(狎昵关系)
- Alternative Classes with Different Interfaces(异曲同工的类)
- Incomplete Library Class(不完美的程序库类)
- Data Class(纯稚的数据类)
- Refused Bequest(被拒绝的遗贈)
- Comments(过多的注释)
- 章节四 构筑测试体系
- 自我测试代码的价值
- JUnit测试框架
- 添加更多测试
- 章节五 重构名录
- 重构的记录格式
- 寻找引用点
- 这些重构准则有多成熟
- 章节六 重新组织你的函数
- Extract Method(提炼函数)
- Inline Method(将函数内联化)
- Inline Temp(将临时变量内联化)
- Replace Temp with Query(以查询取代临时变量)
- Introduce Explaining Variable(引入解释性变量)
- Split Temporary Variable(剖解临时变量)
- Remove Assignments to Parameters(移除对参数的赋值动作)
- Replace Method with Method Object(以函数对象取代函数)
- Substitute Algorithm(替换你的算法)
- 章节七 在对象之间搬移特性
- Move Method(搬移函数)
- Move Field(搬移值域)
- Extract Class(提炼类)
- Inline Class(将类内联化)
- Hide Delegate(隐藏「委托关系」)
- Remove Middle Man(移除中间人)
- Introduce Foreign Method(引入外加函数)
- Introduce Local Extension(引入本地扩展)
- 章节八 重新组织数据
- Self Encapsulate Field(自封装值域)
- Replace Data Value with Object(以对象取代数据值)
- Change Value to Reference(将实值对象改为引用对象)
- Replace Array with Object(以对象取代数组)
- Replace Array with Object(以对象取代数组)
- Duplicate Observed Data(复制「被监视数据」)
- Change Unidirectional Association to Bidirectional(将单向关联改为双向)
- Change Bidirectional Association to Unidirectional(将双向关联改为单向)
- Replace Magic Number with Symbolic Constant(以符号常量/字面常量取代魔法数)
- Encapsulate Field(封装值域)
- Encapsulate Collection(封装群集)
- Replace Record with Data Class(以数据类取代记录)
- Replace Type Code with Class(以类取代型别码)
- Replace Type Code with Subclasses(以子类取代型别码)
- Replace Type Code with State/Strategy(以State/strategy 取代型别码)
- Replace Subclass with Fields(以值域取代子类)
- 章节九 简化条件表达式
- Decompose Conditional(分解条件式)
- Consolidate Conditional Expression(合并条件式)
- Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
- Remove Control Flag(移除控制标记)
- Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件式)
- Replace Conditional with Polymorphism(以多态取代条件式)
- Introduce Null Object(引入Null 对象)
- Introduce Assertion(引入断言)
- 章节十一 处理概括关系
- Pull Up Field(值域上移)
- Pull Up Method(函数上移)
- Pull Up Constructor Body(构造函数本体上移)
- Push Down Method(函数下移)
- Push Down Field(值域下移)
- Extract Subclass(提炼子类)
- Extract Superclass(提炼超类)
- Extract Interface(提炼接口)
- Collapse Hierarchy(折叠继承关系)
- Form Template Method(塑造模板函数)
- Replace Inheritance with Delegation(以委托取代继承)
- Replace Delegation with Inheritance(以继承取代委托)
- 章节十二 大型重构
- 这场游戏的本质
- Tease Apart Inheritance(梳理并分解继承体系)
- Convert Procedural Design to Objects(将过程化设计转化为对象设计)
- Separate Domain from Presentation(将领域和表述/显示分离)
- Extract Hierarchy(提炼继承体系)
- 章节十三 重构,复用与现实
- 现实的检验
- 为什么开发者不愿意重构他们的程序?
- 现实的检验(再论)
- 重构的资源和参考资料
- 从重构联想到软件复用和技术传播
- 结语
- 参考文献
- 章节十四 重构工具
- 使用工具进行重构
- 重构工具的技术标准(Technical Criteria )
- 重构工具的实用标准(Practical Criteria )
- 小结
- 章节十五 集成
- 参考书目