新書推薦:

《
地缘政治与战争:中国历史变局3000年
》
售價:HK$
85.8

《
追踪进化论 在游戏中读懂科学史 《龙与地下城》玩家打造 沉浸式体验进化论 附赠精美计分器
》
售價:HK$
86.9

《
消逝的韩光:华丽韩剧背后的血汗与悲鸣
》
售價:HK$
69.3

《
大学问·从“分治”到“整合”:明清湘黔边墙历史演进与结构变迁
》
售價:HK$
85.8

《
众神:四万年的人、物与信仰
》
售價:HK$
184.8

《
汗青堂丛书159·欧洲的熔炉:意大利文艺复兴与西方的崛起
》
售價:HK$
101.2

《
凌空之魂:五十岚大介短篇集 赠猫咪方银卡+鸮女明信片 自然寓言怪谈异色人外兽人都市奇谭漫画
》
售價:HK$
47.1

《
女性曼陀罗心理成长涂画书
》
售價:HK$
75.9
|
| 編輯推薦: |
|
软件开发经典著作,用实例教会读者如何进行代码重构,提升程序的表达力和可维护性。
|
| 內容簡介: |
|
书中清晰揭示了重构的过程,解释了重构的原理和实践方式,并给出了何时以及何地应该开始挖掘代码以求改善。书中给出了60多个可行的重构,每个重构都介绍了一种经过验证的代码变换手法的动机和技术。本书提出的重构准则将帮助开发人员一次一小步地修改代码,从而减少了开发过程中的风险。本书适合软件开发人员、项目管理人员等阅读,也可作为高等院校计算机及相关专业师生的参考读物。依次解释什么是重构,为什么要重构,如何通过“坏味道”识别出需要重构的代码,以及如何在实践中成功实施重构(无论用的是什么编程语言)。●理解重构的过程和重构的基本原则;●快速有效地应用各种重构手法,提升程序的表达力和可维护性;●识别代码中能指示出需要重构的地方的“坏味道”●深入了解各种重构手法,每个手法都包含解释、动机、做法和范例4个部分;●构建稳固的测试,以支持重构工作的开展;●理解重构过程的权衡取舍以及重构存在的挑战等。本书凝聚了软件开发社区专家多年摸索而获得的宝贵经验,书中所蕴涵的思想和精华,值得反复咀嚼,而且往往能够常读常新。
|
| 關於作者: |
|
马丁·福勒,马丁?福勒(MartinFowler)软件开发大师,ThoughtWorks科学家。他是一位作家、演说者、咨询师。他致力于改善企业级的软件设计,对设计以及支撑设计的工程实践孜孜以求。他在重构、面向对象分析设计、模式、XP和UML等领域都有贡献,著有《重构》《分析模式》《领域特定语言》等经典著作。译者简介熊节 在IT行业已经打拼了18年,在金融、零售、政府、电信、制造业等行业的信息化建设方面有着丰富经验,是中国IT业敏捷浪潮的领军人物。熊节拥有利物浦大学MBA学位。林从羽 ThoughtWorks软件开发工程师,曾服务于国内外多家大型企业,致力于帮助团队更快更好地交付可工作的软件。拥抱敏捷精神,TDD爱好者,纯键盘工作者。
|
| 目錄:
|
目录
第 1 章 重构-入门示例1
1.1 示例介绍2
1.2 对示例代码的评价4
1.3 重构的前提:单元测试用例5
1.4 分解statement函数5
1.5 阶段性展示:大量函数嵌套18
1.6 模块化拆分:计算模块与输出模块19
1.7 阶段性展示:两阶段文件隔离26
1.8 多态:按类型封装计算逻辑28
1.9 阶段性展示:多态下的计算模块34
1.10 本章小结35
第 2 章 重构的原则37
2.1 什么是重构38
2.2 重构:身兼双职39
2.3 重构的意义39
2.4 重构的时机41
2.5 重构的挑战45
2.6 重构、架构和Yagni49
2.7 重构与更广泛的软件开发过程50
2.8 重构与性能51
2.9 重构的起源53
2.10 自动化重构54
2.11 延伸阅读56
第 3 章 代码中的“异味”(重构的信号)57
3.1 神秘的命名58
3.2 重复代码59
3.3 函数过长59
3.4 参数列表过长60
3.5 全局数据60
3.6 可变数据61
3.7 发散性变化62
3.8 霰弹式修改62
3.9 依恋情结63
3.10 数据泥团63
3.11 基本类型偏执64
3.12 重复的switch语句64
3.13 循环65
3.14 冗赘的元素65
3.15 夸夸其谈通用性65
3.16 临时字段65
3.17 过长的调用链66
3.18 中间人66
3.19 内幕交易66
3.20 大类67
3.21 异曲同工的类67
3.22 数据类67
3.23 被拒绝的馈赠68
3.24 注释68
第 4 章 构建测试用例69
4.1 测试用例的价值70
4.2 待测试的示例代码71
4.3 第一个测试74
4.4 继续添加测试76
4.5 修改测试数据78
4.6 边界用例(极端用例)78
4.7 测试远不止于此81
第 5 章 重构手法清单82
5.1 重构的讲解格式83
5.2 重构手法的选择84
第 6 章 第一组重构手法85
6.1 提炼函数法(extract function)86
6.2 内联函数法(inline function)93
6.3 提炼变量法(extract variable)95
6.4 内联变量法(inline variable)98
6.5 修改函数声明法(change function declaration)99
6.6 封装变量法(encapsulate variable)105
6.7 修改变量名称法(rename variable)109
6.8 参数对象引入法(introduce parameter object)110
6.9 函数组合成类法(combine functions into class)114
6.10 函数组合成转换法(combine functions into transform)117
6.11 阶段拆分法(split phase)121
第 7 章 封装127
7.1 封装记录法(encapsulate record)128
7.2 封装集合法(encapsulate collection)134
7.3 对象替代基本类型法(replace primitive with object)138
7.4 函数替代临时变量法(replace temp with query)141
7.5 提炼新类法(extract class)143
7.6 内联类法(inline class)146
7.7 隐藏委托法(hide delegate)148
7.8 移除中间人(remove middle man)150
7.9 算法替换法(substitute algorithm)151
第 8 章 迁移特性153
8.1 函数迁移法(move function)154
8.2 字段迁移法(move field)161
8.3 代码迁移至函数法(move statements into function)165
8.4 代码迁移至调用者法(move statements to callers)168
8.5 函数调用替换内联代码法(replace inline code with function call)172
8.6 移动语句法(slide statements)173
8.7 拆分循环法(split loop)176
8.8 管道替代循环法(Replace Loop with Pipeline)179
8.9 移除无用代码(remove dead code)183
第 9 章 组织数据185
9.1 拆分变量法(split variable)186
9.2 修改字段名称(rename field)188
9.3 查询替代衍生变量法(replace nested conditional with guard clauses)191
9.4 引用对象转为值对象(change reference to value)193
9.5 值对象转为引用对象(change value to reference)196
第 10 章 简化条件逻辑199
10.1 分解条件法(decompose conditional)200
10.2 合并条件表达式法(consolidate conditional expression)202
10.3 卫语句替代嵌套条件表达式法(replace nested conditional with guard clauses)204
10.4 多态替代条件表达式法(replace conditional with polymorphism)208
10.5 引入特例法(introduce special case)220
10.6 引入断言法(introduce assertion)230
第 11 章 重构API232
11.1 查询函数—更新函数分离法(separate query from modifier)233
11.2 函数参数化(parameterize function)236
11.3 移除标志参数法(remove flag argument)238
11.4 保留完整对象法(preserve whole object)242
11.5 查询替代参数法(replace parameter with query)245
11.6 参数替代查询法(replace query with parameter)248
11.7 移除赋值函数法(remove setting method)251
11.8 工厂函数替代构造函数法(replace constructor with factory function)252
11.9 命令替代函数法(replace function with command)254
11.10 函数替代命令法(replace command with function)259
第 12 章 处理继承体系263
12.1 函数上移法(pull up method)264
12.2 字段上移法(pull up field)266
12.3 构造函数本体上移法(pull up constructor body)267
12.4 函数下移法(push down method)270
12.5 字段下移法(push down field)270
12.6 子类替代类型法(replace type code with subclasses)271
12.7 移除子类法(remove subclass)277
12.8 提炼父类法(extract superclass)281
12.9 折叠继承法(collapse hierarchy)284
12.10 委托替代子类法(replace subclass with delegate)285
12.11 委托替代父类法(replace superclass with delegate)298
|
| 內容試閱:
|
第一版前言
“重构”最初是在Smalltalk圈子中构思出来的,但不久之后就进入了其他编程语言的阵营。由于重构是框架开发不可或缺的一部分,因此“框架工作者”在谈论他们的技术时,经常会提到“重构”一词。当他们改进类层次结构时,当他们需要删除多少行代码时,都会提到这个术语。框架工作者深知,好的框架不是一蹴而就的—随着经验的积累,框架需要不断地进行优化和调整。他们还知道,代码的读取和修改频率高于新代码的编写频率。保持代码可读性和易修改性的关键就是重构—特别是对于框架而言。当然,对于一般软件而言也是如此。
那么,重构有什么弊端吗?当然,“重构是有风险的”。它需要修改代码,这可能会引入细微的Bug。如果重构做得不正确,可能会导致代码回滚到数周之前的版本。此外,非正式或临时实施重构时,风险会更大。重构是基于对代码的深入研究的,研究的过程中你会发现待重构的点,研究越深入,发现的待重构的点就越多……做出的改变也就越多。最终,你会陷入一个无法自拔的深渊。为了避免自掘坟墓,必须系统地进行重构。当我和合著者撰写《设计模式》时,曾提到:“设计模式为重构提供了目标。”但是,确定目标只是问题的一部分;转换代码以达到目标则是另一个挑战。
Martin Fowler和其他作者通过阐明重构过程,为面向对象的软件开发做出了宝贵的贡献。本书解释了重构的原则和最佳实践,并指出了何时何地应该开始深入研究代码以改进它。本书的核心是一份全面的重构目录。每个重构都描述了经过验证的代码转换的动机和做法。一些重构手法(例如提取方法或移动字段)可能看起来会很明显。
理解此类重构的机制是以规范的方式进行重构的关键。本书中的重构将帮助你一步一步地更改代码,从而降低设计演变的风险。你可以将这些重构及其名称添加到开发工具书中。
我第一次体验规范的、一步一步的重构是在与Kent Beck在30000英尺的高空进行结对编程时(后文会提及在飞机上相遇),它确保我们一步一步地应用本书目录中的重构手法。这种规范的重构所带来的效果让我感到惊讶,我不仅对最终代码的信心增加了,而且感觉压力也减轻了。我强烈建议读者尝试这些重构:“您和您的代码都会变得更好。”
—Erich Gamma, Object Technology International, Inc.
1999年1月
第二版前言
曾经有一位技术顾问查看了一个开发项目的代码,当他浏览系统中心的类层次结构时,发现其设计得相当混乱。较高级别的类(父类)对一些函数的工作方式做出了某些假设,希望子类可以直接使用这些函数。然而,这些代码并不适合所有的子类,多数子类会选择覆写这些函数,只要对这些函数稍微进行修改就能避免子类的覆写。也有一些地方,父类的函数无法清晰地表达函数的功能意图,并且存在很多重复代码。还有一些地方,几个子类对可以明显向上移动的代码(公共代码)做了重复的事情。
顾问建议项目管理层查看并清理代码,但项目管理层并未重视这个建议。代码既然可以工作,而且项目进度紧张,经理们就会敷衍地说,会在稍后的某个时候处理这个问题。
顾问还向负责开发的程序员展示了这些问题,程序员们还是很敏锐的,了解了问题所在。这其实不是程序员的错,有时,程序员需要一双新的眼睛来发现问题。因此,程序员花了一两天的时间对层次结构进行了优化。完成后,他们删除了层次结构中一半的代码,依然能够满足项目需求。他们对结果很满意,发现添加新类和在系统其他部分使用这些类都变得更快、更容易了。
可项目管理层不满意,时间很紧,还有很多工作要做,这两位程序员花了两天时间做的工作,对系统几个月后必须提供的众多功能没有任何帮助,而且旧代码运行得很好……管理层认为,唯一的好处仅是设计“纯粹”了一点儿,代码“干净”了一点儿。管理层认为,项目必须交付能运行的代码,而不是让学者满意的代码。顾问建议对系统的其他核心部分进行类似的清理,这可能会使项目暂停一两周,但管理层依旧表示:“所有这些都只是为了让代码看起来更好,对项目交付没有任何好处。”
大家对这个故事有什么感想?顾问建议进一步清理是正确的吗?还是遵循那句古老的工程格言,“如果能运行,就不要修改它?”
我必须承认我有些偏见,我就是那位顾问。6个月后,该项目失败了,很大程度上是因为代码太复杂,无法调试或调整到可被接受的性能。
新的顾问Kent Beck被请来重启该项目—这项工作涉及从头开始重写几乎整个系统。他做了几件不同的事情,但最重要的变化之一是坚持使用重构不断清理代码。团队效率的提高以及重构所起的作用激励我编写了本书的第一版—这样我就可以传递Kent和其他人通过使用重构来提高软件质量所获得的知识。
从那时起,重构就成为编程词汇中公认的一部分,而原著也经受住了考验。然而,对于一本编程书来说,18年已经过时了,所以我觉得是时候重写它了。这样做让我重写了书中的几乎每一页,但从某种意义上说,几乎没有什么变化,因为重构的本质是一样的,至少大多数关键的重构手法本质上依旧保持不变。但我确实希望此次的重写能帮助更多的人学会如何有效地进行重构。
什么是重构
重构是改变软件系统的过程。重构不会改变代码的外部行为,但会改善其内部结构。这是一种规范的代码清理方法,可最大限度地减少引入错误的机会。本质上,重构是一种在编写代码后改进代码的设计。
“编写代码后改进设计”,这是一个奇怪的措辞。在软件开发的大部分历史中,大多数人认为我们先设计,只有设计完成后我们才能编码。随着时间的推移,代码将被修改,系统的完整性(根据该设计的结构)会逐渐消失。代码慢慢地从工程沦为易被黑客入侵的代码。
重构与这种做法相反。通过重构,我们可以将糟糕的甚至混乱的设计重新加工成结构良好的代码。每个步骤都很简单,甚至过于简单。我将字段从一个类移到另一个类,将一些代码从方法中取出以使其成为自己的方法,或者将一些代码向上或向下推送到层次结构中。然而,这些小变化的累积效应可以从根本上改善设计。这与软件衰退的概念完全相反。
通过重构,工作平衡发生了变化。我发现设计不是在开发过程中某一阶段发生的,而是在开发过程中不断发生的。在构建系统的过程中,我学会了如何改进设计。这种互动的结果是一个程序的设计在开发过程中能够一直保持良好的状态。
这本书里有什么
本书是一本重构指南,是为程序开发从业者编写的。我的目的是向您展示如何以可控且高效的方式进行重构。本书想要介绍的重构方式是,不在代码中引入错误且有条不紊地改进其结构的方式。
传统上,一本书以介绍书籍的主题或定义开始,我原则上同意这一点,但我发现很难通过泛泛的讨论或定义来介绍重构,所以我从一个例子开始介绍。第1章采用一个具有一些常见设计缺陷的小程序,并将其重构为一个更易于理解和更改的程序。这将向您展示重构的过程和许多有用的重构手法。想了解某个重构的真正含义,可以阅读专门讲解该重构手法的关键章节。
在第2章中,我将介绍重构的一般原则、一些定义以及进行重构的原因,同时概括重构过程中的一些挑战。在第3章中,KentBeck帮助我描述了如何查找代码的异味以及如何通过重构来清除它们。测试在重构中起着非常重要的作用,因此第4章描述了如何在代码中构建测试。
本书的核心—重构清单贯穿了后续其他的章节。虽然这不是一个全面的清单,但它涵盖了大多数开发人员可能需要的关键重构手法。它源于我在20世纪90年代后期学习重构时所做的笔记,我现在仍在使用这些笔记。当我想做某件事时,例如阶段拆分法(详见6.11节),清单会提醒我如何以安全、循序渐进的方式去做。我希望这是本书中你会经常回顾的部分。
JavaScript案例
与软件开发的大多数技术领域一样,代码示例对于说明概念非常重要。但是,不同语言中的重构可能会有细微差别,有时,一种语言会迫使我注意某些特定的事情,但重构的核心元素仍保持不变。
我选择JavaScript来说明这些重构,因为我觉得这种语言对大多数人来说都是可读的。但是,无论你当前使用的是什么语言,你都不应该觉得很难适应重构。我尽量不使用该语言的任何较复杂的部分,因此你可以仅凭对JavaScript的粗略了解就能理解重构。我使用JavaScript不代表对该语言的任何宣传或认可。
虽然我使用JavaScript作为示例,但这并不意味着本书中的技术仅限于JavaScript。本书的第一版使用了Java,许多程序员发现它很有用,即使他们从未编写过一个Java类。我确实尝试过使用十几种不同的语言作为示例来说明这种普遍性,但我觉得这会让读者感到困惑。不过,这本书是为使用任何语言的程序员编写的。除了示例部分,我没有对该语言做任何假设。我希望读者能够吸收我的一般性评论并将其应用于正在使用的语言。事实上,我希望读者能够采用JavaScript示例并使其适应正在使用的语言。
这意味着,除了讨论具体示例外,当我讨论“类”“模块”“函数”等时,会使用这些术语的一般编程含义,而不是JavaScript语言模型的特定术语。
我使用JavaScript作为示例语言,也意味着我会尽量避免使用那些常规JavaScript程序员不太熟悉的JavaScript风格。这不是一本“JavaScript重构”书,相反,这是一本恰好使用JavaScript的通用重构书。有许多有趣的重构特定于JavaScript(例如从回调重构到承诺,再到异步/等待),但它们超出了本书的范围。
书籍受众群体
我把这本书的目标读者定位为专业程序员—以编写软件为生的人。书中的示例和讨论包含大量需要阅读和理解的代码。这些示例是用JavaScript编写的,但应该适用于大多数语言。我希望有一定经验的程序员来理解这本书的内容,但也不需要太多经验就可以理解。
虽然本书的主要目标是帮助程序员学习重构技术,但本书对于已经了解重构的人来说也很有价值—它可以用作教学辅助工具。在这本书中,我花了很多精力来解释各种重构的工作原理,因此,经验丰富的开发人员可以使用这些材料来指导他们的同事。
虽然本书的重点是代码重构,但重构对系统设计也有很大的影响,高级设计师和架构师必须了解重构的原则并在项目中使用它们,重构最好由受人尊敬且经验丰富的开发人员来介绍,这样的开发人员最能理解重构背后的原则,并将这些原则应用到特定的场景。当您使用JavaScript以外的语言时尤其应当如此,因为您必须将我给出的示例改编为其他语言。
以下是如何在不阅读所有内容的情况下充分利用本书的方法。
如果您想了解什么是重构,请阅读第1章示例。
如果您想了解为什么要重构,请阅读前两章。它们将告诉您什么是重构以及为什么要这样做。
如果您想找到应该重构的地方,请阅读第3章。它会告诉您需要重构的迹象。
如果您真的想进行重构,请完整阅读前四章,然后阅读书籍的目录,以大致了解书中的内容,您不必了解所有细节。当您真正需要进行重构时,再详细阅读重构章节并使用它。目录是一个参考,您可能不想一口气读完它。
编写本书的一个重要部分是命名各种重构手法。术语有助于我们沟通,因此当一个开发人员建议另一个开发人员将一些代码提取到函数中,或者将一些计算拆分为不同的阶段时,双方都能理解对提炼函数法(详见6.1节)和阶段拆分法(详见6.11节)的引用。这些词汇表还有助于选择自动重构。
在别人奠定的基础上继续发展
我首先要说的是,这本书让我受益匪浅,感谢那些在20世纪90年代开创了重构领域的人们。正是基于他们的知识,才激励并启发了我撰写本书的第一版,尽管已经过去了很多年,但我仍然要承认他们奠定的基础。理想情况下,他们中的一位应该撰写第一版,但最终我成了有时间和精力的人。
重构的两位早期主要支持者是Ward Cunningham和Kent Beck。他们在早期将其作为开发的基础,并调整了他们的开发流程。特别想要提一点,正是与Kent的合作让我看到了重构的重要性,这一灵感直接促成了我撰写本书。
Ralph Johnson领导着伊利诺伊大学香槟分校的一个团队,该团队以对面向对象技术的实际贡献而闻名。Ralph长期以来一直是重构的拥护者,他的几位学生也为该领域做出了重要贡献。比尔·奥普代克(Bill Opdyke)在他的博士论文中撰写了第一份关于重构的详细书面著作。约翰·布兰特(JohnBrant)和唐·罗伯茨(Don Roberts)不只写文字,还创建了第一个自动重构工具“重构浏览器”,用于重构Smalltalk程序。
自本书第一版出版以来,许多人推动了重构领域的发展。特别是那些将自动重构添加到开发工具中的人,给程序员的工作带来了巨大的便利。我很容易想当然地认为,我可以用一个简单的按键序列重命名一个广泛使用的函数,但这种便利依赖IDE团队的努力,他们的工作帮助了我们所有人。
致谢
即使有那么多研究可以借鉴,我仍需要很多帮助才能写这本书。第一版很大程度上借鉴了Kent Beck的经验和鼓励。他首先向我介绍了重构,鼓励我写笔记来记录重构,并帮助我将它们整理成完整的文章,同时,他提出了“代码异味”的想法。我常常觉得,如果我们不是在写极限编程的基础书,他写的第一版会比我写得更好。
我认识的所有技术书籍作者都提到技术审阅者给了他们很大的帮助。我们都写过有很多缺陷的作品,只有我们的同行作为审阅者才能发现。我自己不做太多技术审阅工作,部分原因是我认为自己不擅长这项工作,所以我非常钦佩那些接受这项工作的人。审阅别人的书甚至赚不到一分钱,所以这样做是一种慷慨的行为。
当我开始认真写这本书时,我创建了一个顾问邮件列表,以便他们给我反馈。随着我取得进展,我将新材料的草稿发送给这个小组并征求他们的反馈。我要感谢以下人员在邮件列表中发布他们的反馈:Arlo Belshee、Avdi Grimm、Beth Anders-Beck、Bill Wake、Brian Guthrie、Brian Marick、Chad Wathington、Dave Farley、David Rice、Don Roberts、Fred George、Giles Alexander、Greg Doench、Hugo Corbucci、Ivan Moore、James Shore、Jay Fields、Jessica Kerr、Joshua Kerievsky、Kevlin Henney、Luciano Ramalho、Marcos Brizeno、Michael Feathers、Patrick Kua、Pete Hodgson、Rebecca Parsons和Trisha Gee。
在这群人中,我特别想强调Beth Anders-Beck、James Shore和Pete Hodgson在JavaScript方面给予我的特别帮助。
在完成初稿后,我将其发送出去做进一步审查,因为我想让一些新人来审视整个草稿。William Chargin和Michael Hunger都提供了非常详细的审阅意见。我还从Bob Martin和Scott Davis那里得到了许多有用的评论。Bill Wake在邮件列表中对初稿进行了全面审阅,为我的书籍锦上添花。
Thought Works的同事不断为我的写作提供想法和反馈,无数的问题、评论和观察激发了我对这本书的思考和写作。在Thought Works工作的好处之一是,他们允许我花大量的时间进行写作。特别感谢我们的首席技术官Rebecca Parsons,他经常与我交谈,并提出新的想法。
在Pearson,Greg Doench是本书的策划编辑,负责书籍出版过程中的许多问题;Julie Nahil是本书的执行编辑。我很高兴再次与Dmitry Kirsanov合作进行文字编辑,与Alina Kirsanova合作进行排版和索引编制。
|
|