大话重构连载7:重构是一系列的等量变换

系统重构要求我们对代码的每一步修改,都不能改变软件的外部行为,因此在系统重构中的所有方法,都是一种代码的等量变换。重构的过程,就好像在做数学题,一步一步地进行算式的等量变换。经过一系列等量变换,最终的结果虽然在形式上与原式不一样,但通过计算可以得到与原式完全相同的结果。

这种等量变换对于重构来说非常重要,它使得我们进行重构以后,程序还是那些程序,代码还是那些代码。但是,等量变换不等于原地踏步。正如矩阵通过等量变换可以得到方程组的解,微积分可以通过等量变换计算最终的结果,重构通过等量变换,在保证代码正确的同时,可以使程序结构得到优化。为了说明系统重构中的这种等量变换,我们来看看一个简单的例子,原始程序是这样的:

 1 public class HelloWorld {
 2     public String sayHello(Date now, String user){
 3         Calendar c;
 4         int h;
 5         String s = null;
 6         c = Calendar.getInstance();
 7         c.setTime(now);
 8         h = c.get(Calendar.HOUR_OF_DAY);
 9         if(h>=6 && h<12){
10             s = "Good morning!";
11         }else if(h>=12 && h<19){
12             s = "Good afternoon!";
13         }else{
14             s = "Good night!";
15         }
16         s = "Hi, "+user+". "+s;
17         return s;
18     }
19 }

这是一个非常简单的HelloWorld程序,写得简单是为了大家更容易看懂程序的变换过程。这个程序虽然简单却符合遗留系统的许多特点:没有注释、顺序编程、没有层次、聚合度低,等等。因此我们进行了初步重构,增加注释、调整顺序、重命名变量、进行分段:

 1 /**
 2  * The Refactoring‘s hello-world program
 3  * @author fangang
 4  */
 5 public class HelloWorld {
 6     /**
 7      * Say hello to everyone
 8      * @param now
 9      * @param user
10      * @return the words what to say
11      */
12     public String sayHello(Date now, String user){
13         //Get current hour of day
14         Calendar calendar = Calendar.getInstance();
15         calendar.setTime(now);
16         int hour = calendar.get(Calendar.HOUR_OF_DAY);
17
18         //Get the right words to say hello
19         String words = null;
20         if(hour>=6 && hour<12){
21             words = "Good morning!";
22         }else if(hour>=12 && hour<19){
23             words = "Good afternoon!";
24         }else{
25             words = "Good night!";
26         }
27         words = "Hi, "+user+". "+words;
28         return words;
29     }
30 }

然后将两段注释中的代码分别提取出来形成getHour()与getSecondGreeting()函数:

 1 /**
 2  * The Refactoring‘s hello-world program
 3  * @author fangang
 4  */
 5 public class HelloWorld {
 6     /**
 7      * Say hello to everyone
 8      * @param now
 9      * @param user
10      * @return the words what to say
11      */
12     public String sayHello(Date now, String user){
13         int hour = getHour(now);
14         return "Hi, "+user+". "+getSecondGreeting(hour);
15     }
16
17     /**
18      * Get current hour of day.
19      * @param now
20      * @return current hour of day
21      */
22     private int getHour(Date now){
23         Calendar calendar = Calendar.getInstance();
24         calendar.setTime(now);
25         return calendar.get(Calendar.HOUR_OF_DAY);
26     }
27
28     /**
29      * Get the second greeting.
30      * @param hour
31      * @return the second greeting
32      */
33     private String getSecondGreeting(int hour){
34         if(hour>=6 && hour<12){
35             return "Good morning!";
36         }else if(hour>=12 && hour<19){
37             return "Good afternoon!";
38         }else{
39             return "Good night!";
40         }
41     }
42 }

通过这个例子我们可以看到,将没有先后顺序的语句调整编写顺序是一种等量变换,将语句中某段相对独立的语句提取出来形成一个函数,而让原语句调用这个函数,也是一种等量变换。除此之外,调整函数名称、修改变量名称等等,都是等量变换。等量变换,程序还是那些程序,执行的结果还是那些结果,但程序组织结构发生了变化,变得更加可读、可维护、易变更了,这就是重构的意义。

将密密麻麻的程序代码按照功能划分在数个函数中,可以有效地提高代码的可读性;将程序中各种各样的变量和函数合理地予以命名,并在函数头或定义处适时地进行注释,也是在提高代码可读性;将各种各样品种繁多的函数恰当地分配到各自的对象中合理地组织起来,则是在有效提高系统的可维护性与易变更性。这些对于一个遗留系统的日常维护与生命延续都是非常有帮助的。

大话重构连载首页:http://www.cnblogs.com/mooodo/p/talkAboutRefactoringHome.html

特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!

大话重构连载7:重构是一系列的等量变换,布布扣,bubuko.com

时间: 2024-12-24 08:30:57

大话重构连载7:重构是一系列的等量变换的相关文章

大话重构7:重构是一系列的等量变换

毫无疑问,系统重构是一件如履薄冰.如坐针毡.你必须时时小心应对的工作,你就像走在钢丝上的人,每一步你都必须要保证正确,一个不经意的失误就可能让你万劫不复.尽管如此,只要你掌握了正确的方法,即使站在钢丝上也能如履平地,而这个正确的方法,就是那些被证明是正确的重构方法.说了那么多,你一定开始好奇,系统重构到底都是一些什么方法呢?行了,我也就不卖关子了,我们来看看重构方法工具箱里都有些什么东东. 系统重构要求我们对代码的每一步修改,都不能改变软件的外部行为,因此在系统重构中的所有方法,都是一种代码的等

大话重构连载16:超级大函数

事情总是这样的:当我们对一个遗留系统一忍再忍,再忍,忍,还要忍--终于积攒到某一天,实在忍无可忍了,拍案而起,不能再忍了,重构!!!事情就这样发生了.然而,在这时你突然发现,重构的工作千头万绪,真不知从何开始.堆积如山的问题此起彼伏,期望修改的设计思绪万千.这里有个想法,那里有个思路,什么都想做,却什么都做不了,真是脑子里一团乱麻.这时候,没有一个合理的步骤,清晰的计划,瞎干蛮干是十分危险的,它会为你的重构带来不可预期的未来.无数次的经验告诉我,不论是什么系统,采用什么架构,从分解大函数开始,肯

大话重构连载首页

<大话重构>这本书是我写的第一本书,从今天起我将通过连载的形式逐渐跟大家分享. 这本书让你: 告别游击队转变为正规军. 远离劣质代码走向精妙设计 真正明确专业级的软件开发是如何的 真正明确重构是如何一步一步进行的 高效重构七步曲.面对实践不卡壳 让遗留系统维护不再是你的梦魇 读完这本书以后: 需求变更不再纠结.重构让你润物细无声地容纳它们 超越代码级的重构,从各个层面深度领略重构之美 自己主动化測试不再是梦想.重构让自己主动化測试走你 又一次审视熟悉而陌生的技术.将碎了一地的它们又一次铆合在一

大话重构连载18:最常见的问题

使用抽取方法,虽然道理十分简单,但实际操作起来却并不是那么容易的.完成抽取方法最大的困难,就是如何处理抽取函数与原函数的数据交换.如同将一颗大树从土壤里拔出来,盘根错节的根茎,那是剪不断理还乱.当代码还没有被抽取出来之前,它们与其它程序都是在一个函数的内部,因此各个代码段可以毫无顾忌地相互交互数据.但当我们将代码从原函数中抽取出来时,抽取出来的代码与原函数中的代码就形成了一道墙,要交换的数据只能通过参数与返回值进行交互,这将给我们带来诸多麻烦. 将代码从原函数中抽取出来,放进新的函数中,首先就会

大话重构连载19:大对象的演化过程

很好,我们终于迈出了重构的第一步,而这第一步我们瞄准了代码问题的重灾区——超级大函数.超级大函数之所以是代码问题的重灾区,就是因为它们往往难于阅读.难于维护.面对大函数我们采取的办法是拆分,以功能为核心将其拆分成一个一个独立的函数.拆分后的程序变得易于阅读了,因为要读懂程序你不再需要读完所有代码,选择性的读取那些顶级函数,只需了了数行代码,你就可以明白整个程序. 但是,当我们将数千行的大函数分解成数十个小函数时,另一个问题出现了.想象一下,数十个函数被杂乱无章地堆放在一个对象中,看看就让人头疼.

大话重构连载15:采用Mock技术完成测试

第五次重构我们引入了数据库的设计,用户信息要从数据库中读取,问候语库存储在数据库中,并支持添加与更新.数据库的引入使自动化测试变得困难了,因为数据状态总是变化着的,而这种变化使得测试过程不能复现,这是我们不愿看到的.因此,我们在设计时将业务与数据库访问分离,形成了UserDao与GreetingRuleDao.此时,我们的设计应当遵从“依赖反转”原则,即将UserDao与GreetingRuleDao设计成接口,并编写它们的实现UserDaoImpl与GreetingRuleDaoImpl.这样

大话重构连载17:抽取方法的实践

说了那么多理论,我们来看看怎样使用抽取方法来重构遗留系统.如前所述,重构的过程首先是阅读程序代码,边阅读边整理程序.将功能相对独立的代码段放在一起,在前面加上注释.调整一些程序的顺序,将相关的代码尽量放在一起,但要保证程序执行的结果不会发生改变.比较典型的,将变量的定义与使用变量的代码放在一起.这个步骤比较实用,因为许多的遗留系统,其代码都有一个坏毛病,就是在程序开始时定义一大堆变量,但要弄清这些变量都用来做什么,却十分困难.边读边调整,将变量的定义逐渐迁移到使用它的代码段中,将大大提高代码可读

大话重构连载8:盘点我们的重构工具箱

下面我们来盘点一下系统重构工具箱里都有什么,也就是看一看系统重构到底都有哪些方法.系统重构总是在不同层次上调整我们的代码,因此重构方法也就分为了多个层次.从总体上看,重构方法分为以下几个层次:方法的重构.对象的重构.对象间的重构.继承体系间的重构.组织数据的重构与体系架构的重构. 前面那个例子我们可以清楚地看到方法的重构过程.方法的重构往往发生在一个对象的内部,是对一个对象内部的优化.从这个例子中,我们首先看到了增加注释.调整顺序.重命名变量.进行分段等操作,这些虽然不是什么重构方法,却是我们进

大话重构连载12:你不能没有保险索

通过前面的描述你已经对重构中"小步快跑"的开发模式有了一个清楚的认识.学会和习惯小步快跑的开发模式,对于重构工作极其重要,因为它让这种大范围.大幅度修改代码的重构工作变得不再像以往那样让人胆战心惊.究其原因,虽然从结果上是在大范围.大幅度调整,但每一步却是小范围.小福度调整,并且能保证每一步都是正确的. 然而,这里有一个非常重要的假设条件,那就是"保证每一步都是正确的",这是怎么保证的呢?就这个问题,我们需要展开来认真讨论讨论. 毫无疑问,系统重构就其结果来看,是在