### 使用工具进行重构
和手工重构相比,自动化工具所支持的重构,给人一种完全不同的感觉。即使有测试套件(test suite)织成的安全网,手工重构仍然是很耗时的工作。正是这个简单的事实造成很多程序员不愿进行重构,尽管他们知道自己应该重构,但毕竟重构的成本太大了。如果能够把重构变得像调整代码格式那么简单,程序员自然也会乐意像整理代码外观那样去整理系统的设计。而这样的整理对代码的可维护性、可复用性和可理解性,都能够带来深远的正面影响。Kent Back 如是说;
Kent Back
Refactoring Browser 将会完全改变你的编程思路。以前你可能会想:『呃,我应该修改这个名字,但……』,现在,所有这些让你烦心的事情都烟消云散了,因为[Refactoring Browser]里头有个菜单选项(menu item )就是专门用来易名的,你只管放心用它就是了。
刚开始使用这个工具时,我按照以前的节奏,走了大概两个小时:我打算进行一项重构,于是抬头望着天空五分钟,然后手工完成重构,然后再一次抬头望天。很快我发现:我必须学会以更大的范围、更快的节奏来考虑重构。现在,开发过程中我大约以一半时间进行重构,另一半时间输入新代码,两者的进行速度几乎完全相同。
由于有了这种级别的工具支持,重构和编程之间的差异愈来愈小了。我们几乎不会再说『我正在编程』或『我正在重构』,我们说得更多的是『把这个函数的这一部分提炼出来,把它推到superclass 去,然后添加一行语句,调用新subclass 中的新函数——我正在开发的那个函数。』由于自动化重构之后无须测试,因此编程与重构之间的差异、「更换帽子」的过程等等尽管仍然存在,但都远不如以前那样明显了。
以Extract Method 这一重要的重构手法为例。如果你要手工进行此一重构, 需要检查的东西相当多。如果使用Refactoring Browser,你只需简单地圈选出你要提炼的段落,然后点选菜单选项(menu item )"Extract Method"就行了。Refactoring Browser 会自动检查被圈选的代码段落是否可以提炼。代码无法提炼的原因可能有以下几点:它可能只包含部分标识符声明,或者可能对某个变量赋值而该变量又被其他代码用到。所有这些情况,你都完全不必担心,因为重构工具会帮助你处理这一切。然后,Refactoring Browser 会计算出新函数所需的参数,并要求你为新函数取一个名称。你还可以决定新函数参数的排列顺序。所有的准备工作都做完以后,Refactoring Browser 会把你圈选的代码从源函数中提炼出来,并在源函数中加上对新函数的调用。随后它会在源函数所属的class 中建立新函数,并以用户指定的名称为新函数命名。整个过程只需15秒种。你可以拿这个时间长短和手工执行Extract Method 各步骤所需时间做个比较,看看自动化重构工具的威力。
随着重构成本的降低,设计错误也不再像从前那样带来昂贵代价了。由于弥补设计错误所需的成本降低了,需要预先做的设计也就更少了。预先设计是一项带有预测性质的工作,因为项目激活之时,需求往往还不明朗。由于设计时尚未编写代码,所以正确的设计方式应该是:尽量简化「需求尚未明朗」的那一部分代码。过去,无论最初的设计方案水平如何,我们都不得不忍受,因为修改设计的代价实在太高了。有了自动化重构工具的帮助,我们可以让设计更具可变性,因为修改设计不再需要付出那么高的代价了。如今,我们可以只对当前完全了解的问题进行设计,因为我们知道以后可以很方便地扩展设计方案以加入额外的灵活性。我们不再需要预测系统未来所有可能的修改。如果发现当前的设计给编程带来麻烦,造成第3章所说的臭味,我们可以很快修改设计,使代码更干净、更可维护。
工具辅助下的重构工作,也影响了测试。拥有自动化重构工具的辅助之后,所需测试少多了,因为很多重构都可以自动进行,无需再做测试。当然,总有一些重构是无法自动进行的,因此测试步骤永远都不可能被完全忽略。经验显示:在自动化重构工具的协助下,我们每天所需运行的测试数量,和在「无自动化重构工具」环境中大致相当,但完成的重构数量则大大增加。
正如Martin 指出,Java 也需要这样的自动化重构工具。以下我们将提出一些准则——只有满足这些准则的自动化重构工具,才是成功的工具。尽管也提到了技术方面的准则,但我们相信,实用性方面的准则更重要得多。
- 译序 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 )
- 小结
- 章节十五 集成
- 参考书目