重构是一系列的等量变换2016-03-17系统重构要求我们对代码的每一步修改,都不能改变软件的外部行为,因此在系统重构中的所有方法,都是一种代码的等量变换。重构的过程,就好像在做数学题,一步一步地进行算式的等量变换。经过一系列等量变换,最终的结果虽然在形式上与原式不一样,但通过计算可以得到与原式完全相同的结果。这种等量变换对于重构来说非常重要,它使得我们进行重构以后,程序还是那些程序,代码还是那些代码。但是,等量变换不等于原地踏步。正如矩阵通过等量变换可以得到方程组的解,微积分可以通过等量变换计算最终的结果,重构通过等量变换,在保证代码正确的同时,可以使程序结构得到优化。为了说明系统重构中的这种等量变换,我们来看看一个简单的例子,原始程序是这样的:
public class HelloWorld {public String sayHello(Date now, String user){Calendar c;int h;String s = null;c = Calendar.getInstance();c.setTime(now);h = c.get(Calendar.HOUR_OF_DAY);if(h>=6 && h<12){s = "Good morning!";}else if(h>=12 && h<19){s = "Good afternoon!";}else{s = "Good night!";}s = "Hi, "+user+". "+s;return s;}}
这是一个非常简单的HelloWorld程序,写得简单是为了大家更容易看懂程序的变换过程。这个程序虽然简单却符合遗留系统的许多特点:没有注释、顺序编程、没有层次、聚合度低,等等。因此我们进行了初步重构,增加注释、调整顺序、重命名变量、进行分段:
/** * The Refactoring"s hello-world program * @author fangang */public class HelloWorld {/** * Say hello to everyone * @param now * @param user * @return the words what to say */public String sayHello(Date now, String user){//Get current hour of dayCalendar calendar = Calendar.getInstance();calendar.setTime(now);int hour = calendar.get(Calendar.HOUR_OF_DAY);//Get the right words to say helloString words = null;if(hour>=6 && hour<12){words = "Good morning!";}else if(hour>=12 && hour<19){words = "Good afternoon!";}else{words = "Good night!";}words = "Hi, "+user+". "+words;return words;}}
然后将两段注释中的代码分别提取出来形成getHour()与getSecondGreeting()函数:
/** * The Refactoring"s hello-world program * @author fangang */public class HelloWorld {/** * Say hello to everyone * @param now * @param user * @return the words what to say */public String sayHello(Date now, String user){int hour = getHour(now);return "Hi, "+user+". "+getSecondGreeting(hour);}/** * Get current hour of day. * @param now * @return current hour of day */private int getHour(Date now){Calendar calendar = Calendar.getInstance();calendar.setTime(now);return calendar.get(Calendar.HOUR_OF_DAY);}/** * Get the second greeting. * @param hour * @return the second greeting */private String getSecondGreeting(int hour){if(hour>=6 && hour<12){return "Good morning!";}else if(hour>=12 && hour<19){return "Good afternoon!";}else{return "Good night!";}}}
通过这个例子我们可以看到,将没有先后顺序的语句调整编写顺序是一种等量变换,将语句中某段相对独立的语句提取出来形成一个函数,而让原语句调用这个函数,也是一种等量变换。除此之外,调整函数名称、修改变量名称等等,都是等量变换。等量变换,程序还是那些程序,执行的结果还是那些结果,但程序组织结构发生了变化,变得更加可读、可维护、易变更了,这就是重构的意义。将密密麻麻的程序代码按照功能划分在数个函数中,可以有效地提高代码的可读性;将程序中各种各样的变量和函数合理地予以命名,并在函数头或定义处适时地进行注释,也是在提高代码可读性;将各种各样品种繁多的函数恰当地分配到各自的对象中合理地组织起来,则是在有效提高系统的可维护性与易变更性。这些对于一个遗留系统的日常维护与生命延续都是非常有帮助的。