日期:2008-04-21  浏览次数:20480 次

 CSDN - 技术频道 - 文档中心 - Java      
  

标题     重构模式1    xxcc(收藏)
  
关键字     重构 模式
  

refactoring Patterns:第一部分     


   
内容:

介绍
什么是Refactoring?
Refactoring原则
参考资料
关于作者


相关内容:

该系列的其他部分




石一楹 (shiyiying@hotmail.com)
浙江大学灵峰科技开发公司技术总监
2001 年 12 月

这是关于refactoring思考的第一部分内容。本文将介绍refactoring的基本概念、定义,同时解释正确、安全进行refactoring需要坚持的几个原则。
介绍
代码太容易变坏。代码总是趋向于有更大的类、更长的方法、更多的开关语句和更深的条件嵌套。重复代码随处可见,特别是那些初看相似细看又不同的代码泛滥于整个系统:条件表达式,循环结构、集合枚举….信息被共享于系统一些关系甚少的组成部分之间,通常,这使得系统中几乎所有的重要信息都变成全局或者重复。你根本不能看到这种代码还有什么良好的设计。(如果有的话,也已经不可辨识了。)

这样的代码难以理解,更不要说对它加以修改。如果你关心系统体系结构、设计,或者是一个好程序,你的第一反应就是拒绝工作于这样的代码。你会说:"这么烂的代码,让我修改,还不如重写。"然而,你不大可能完全重写已经能够甚至是正在运作的系统,你不能保证新的系统能够实现全部的原有功能。更何况,你不是生活在真空,还有更多的投资、交付、竞争压力。

于是你使用一种quick-and-dirty的方法,如果系统有问题,那么就直接找到这个问题,便当地修改它。如果要增加一个新功能,你会从原来的系统中找到一块相近的代码,拷出来,作些修改。对于原来的系统,你想,既然我不能重头写过,而且它们已经在运作,让它去吧。然后,你增加的代码变成了下一个程序员咒骂的对象。系统越来越难以理解,维护越来越困难、越来越昂贵。系统变成了一个十足的大泥球。

这种情况是每一个人都不愿意碰到的,但是奇怪的是,这样的情景一次又一次出现在大多数人的编程生涯中。这是因为我们不知道该如何解决。

解决这个问题的最好办法当然是让它不要发生。然而,要阻止代码的腐化,你需要付出额外的代价。每次在修改或增加代码之前,你都要看一看手上的这些代码。如果它有很好的味道,那么你应该能够很方便地加入新的功能。如果你需要花很长的时间去理解原来的代码,花更长的时间去增加和修改代码。那么,先放下手里的活,让我们来做Refactoring。

什么是Refactoring?
每个人似乎都有自己的Refactoring的定义,尽管他们讲的就是同一件事情。在那么多的定义中,最先对Refactoring进行理论研究的Raloh Johnson的话显然更有说服力:
     Refactoring是使用各种手段重新整理一个对象设计的过程,目的是为了让设计更加灵活并且/或者更可重用。你可能有几个理由来做这件事情,其中效率和可维护性可能是最重要的原因。

Martin Fowler[Fowler]把Refactoring定义为两部分,一部分为名词形式:
     Refactoring(名词): 在不改变可观察行为的前提下,对软件内部结构的改变,目的是使它更易于理解并且能够更廉价地进行改变。

另一部分则是动词形式:
     Refactor(动词): 通过应用一系列不改变软件可观察行为的refactoring来重构一个软件。
Martin Fowler的名词形式就是说Refactoring是对软件内部结构的改变,这种改变的前提是不能改变程序的可观察的行为,这种改变的目的就是为了让它更容易理解,更容易被修改。动词形式则突出Refactor是一种软件重构行为,这种重构的方法就是应用一系列的refactoring。

软件结构可以因为各种各样的原因而被改变,如进行打印美化、性能优化等等,但只有出于可理解性、可修改、可维护目的的改变才是Refactoring。这种改变必须保持可观察的行为,按照Martin的话来说,就是Refactoring之前软件实现什么功能,之后照样实现什么功能。任何用户,不管是终端用户还是其他的程序员,都不需要知道某些东西发生了变化。

Refactoring原则

Two Hats(两顶帽子)
Kent Beck提出这个比方。他说,如果你在使用Refactoring开发软件,你把开发时间分给两个不同的活动:增加功能和refactoring。增加功能时,你不应该改变任何已经存在的代码,你只是在增加新功能。这个时候,你增加新的测试,然后让这些新测试能够通过。当你换一顶帽子refactoring时,你要记住你不应该增加任何新功能,你只是在重构代码。你不会增加新的测试(除非发现以前漏掉了一个)。只有当你的Refactoring改变了一个原先代码的接口时才改变某些测试。

在一个软件的开发过程中,你可能频繁地交换这两顶帽子。你开始增加一个新功能,这时你认识到,如果原来的代码结构更好一点,新功能就能够更方便地加入。因此,你脱下增加功能的帽子,换上refactoring的帽子。一会儿,代码结构变好了,你脱下refactoring的帽子,戴上增加功能的帽子。增加了新功能以后,你可能发现你的代码使得程序的结构难以理解,这时你又交换帽子。

关于两顶帽子交换的故事不断地发生在你的日常开发中,但是不管你带着哪一定帽子,一定要记住带一定帽子只做一件事情。

Unit Test
保持代码的可观察行为不变称为Refactoring的安全性。Refactoring工具用半形式化的理论证明来保证Refactoring的安全性。

但是,要从理论上完全证明系统的可观察行为保持不变,虽然不是说不可能,也是十分困难的。工具也有自己的缺陷。首先,目前对于Refactoring的理论研究并非十分成熟,某些曾经被证明安全的Refactoring最近被发现在特定的场合下并不安全。其次,目前的工具不能很好地支持"非正式"的Refactoring操作,如果你发现一种新的Refactoring技巧,工具不能立即让这种refactoring为你所用。

自动化的测试是检验Refactoring安全性非常方便而且有效的方法。虽然我们不能穷尽整个系统中所有的测试,但如果在Refactoring之前成功的测试现在失败了,我们就会知道刚刚做的Refactoring破坏了系统的可观察行为。自动化测试能够在程序员不进行人工干预的情况下自动检测到这样的行为破坏。

自动化测试中最实用的工具是XUnit系列单元测试框架,该框架最初由Kent Beck和Eric Gamma为Smalltalk社团而开发。

Eric Gamma对测试的重要性曾经有过这样的话:
     你写的测试越少,你的生产力就越低,同时你的代码就变得越不稳定。你越是没有生产力、越缺少准确性,你承受的压力就越大......

下面的片断来自Javaworld,两个Sun开发者展示了它们对单元测试的狂热以及展示了它们扩展单元测试来检查象EJB这样的分布式控件:
     我们从来没有过度测试软件,相反我们很少做得足够。。。但愿测试是软件开发过程中关键但却经常被误解的一部分。对每一个代码单元而言,单元测试确保他自己能够工作,独立于其他单元。在面向对象语言中,一个单元通常,但并不总是,一个类的等价物。如果一个开发者确信