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

下面我们来盘点一下系统重构工具箱里都有什么,也就是看一看系统重构到底都有哪些方法。系统重构总是在不同层次上调整我们的代码,因此重构方法也就分为了多个层次。从总体上看,重构方法分为以下几个层次:方法的重构、对象的重构、对象间的重构、继承体系间的重构、组织数据的重构与体系架构的重构。

前面那个例子我们可以清楚地看到方法的重构过程。方法的重构往往发生在一个对象的内部,是对一个对象内部的优化。从这个例子中,我们首先看到了增加注释、调整顺序、重命名变量、进行分段等操作,这些虽然不是什么重构方法,却是我们进行前期准备的常用技法。随后我们将通过注释分段存放的各个代码段提取出来,形成单独的函数。这种重构方法被称为“抽取方法(Extract Method)”。

随后我们继续重构。仔细观察这个程序我们发现,这个程序内聚性不好。最好用一个DateUtil管理诸如getHour()的方法,用Greeting管理各种问候语及其规则,因此我们进行了如下重构:

/**
 * A utility about time.
 * @author fangang
 */
public class DateUtil {
	/**
	 * Get hour of day.
	 * @param date
	 * @return hour of day
	 */
	public int getHour(Date date){
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		return calendar.get(Calendar.HOUR_OF_DAY);
	}
}

/**
 * All kinds of greeting.
 * @author fangang
 */
public class Greeting {
	/**
	 * Get the first greeting.
	 * @param user
	 * @return Hi, {user}.
	 */
	public String getFirstGreeting(String user){
		return "Hi, "+user+". ";
	}

	/**
	 * Get the second greeting.
	 * @param hour
	 * @return if in the morning, say "Good morning!",
	 * if in the afternoon, say "Good afternoon!",
	 * else say "Good night!".
	 */
	public 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!";
		}
	}
}

/**
 * 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){
		DateUtil dateUtil = new DateUtil();
		int hour = dateUtil.getHour(now);

		Greeting greeting = new Greeting();
		return greeting.getFirstGreeting(user)+
				greeting.getSecondGreeting(hour);
	}
}

这里我们将getHour()从HelloWorld类中抽取出来,放到了DateUtil类中,使HelloWorld类中仅保留一个引用。同时,我们将getFirstGreeting()与getSecondGreeting()从HelloWorld类中抽取出来,放到了Greeting类中,使原程序变为引用。这样的技法我们称之为“抽取类(Extract Class)”。

再仔细观察这一段程序:

	/**
	 * Get the second greeting.
	 * @param hour
	 * @return if in the morning, say "Good morning!",
	 * if in the afternoon, say "Good afternoon!",
	 * else say "Good night!".
	 */
	public 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!";
		}
	}

除了morning,afternoon, night以外,如果我们再增加evening,程序的可扩展性就不好了。因此我们将它们提取出GreetingRule接口,并分别编写了morning, afternoon, night和evening的实现类:

/**
 * Greeting rules interface
 * @author fangang
 */
public interface GreetingRule {
	/**
	 * @param hour
	 * @return whether the rule is right
	 */
	public boolean isRight(int hour);

	/**
	 * @return the greeting words
	 */
	public String getGreeting();
}

/**
 * The greeting in the morning
 * @author fangang
 */
public class MorningGreeting implements GreetingRule {

	/* (non-Javadoc)
	 * @see org.refactoring.helloWorld...GreetingRule#getGreeting()
	 */
	@Override
	public String getGreeting() {
		return "Good morning! ";
	}

	/* (non-Javadoc)
	 * @see org.refactoring.helloWorld...GreetingRule#isRight(int)
	 */
	@Override
	public boolean isRight(int hour) {
		if(hour>=6 && hour<12){
			return true;
		}
		return false;
	}
}

其它几个实现类我就不累牍了,最后将getSecondGreeting()方法改成这样:

<span style="white-space:pre">	</span>/**
	 * Get the second greeting.
	 * @param hour
	 * @return if in the morning, say "Good morning!",
	 * if in the afternoon, say "Good afternoon!",
	 * if in the evening, say "Good evening! ",
	 * else, say "Good night!".
	 */
	public String getSecondGreeting(int hour){
		for(GreetingRule greetingRule : greetingRules){
			if(greetingRule.isRight(hour)){
				return greetingRule.getGreeting();
			}
		}
		throw new RuntimeException("Error when greeting! ");
	}

这种将相似的,或者同类型的代码抽取出来形成接口,以及接口下的多个实现,我们称之为“抽取接口(Extract Interface)”。

看了这些例子你可能会有一个疑问,这样简单的程序搞成这样,是否值得?是的,不错,程序的结构应当与需求的复杂度相适应。简单的需求编写简单的程序,复杂的需求编写复杂的程序。如果将简单的需求,为了玩技术,搞成了复杂的程序,那就是“过度设计”。但这里为了更加清楚的向大家展示重构,又能够使大家不要因为复杂的程序而分心,故而将简单程序过度设计了一把。但在后面我们可以看到,当业务需求逐渐复杂时,我们进行以上这些重构是值得的。

文后附录列出了所有的重构方法,它们都来源于重构经典书籍《重构,改善既有代码的设计》,在日后的时间我们应当反复咀嚼这些方法。

正如武侠大师金庸所说的“无招胜有招”,如此多的重构方法不是要让你去生搬硬套,而是应该对其进行深刻理解以后,最终变成你自己的重构方法。我们在实际工作中不要过于介意我们用了什么重构方法,哪次重构是用的哪个方法,只要是合适的设计就OK。但是,在无招胜有招之前,我们必须要有招,即学会了、理解了各个招式,在实际工作中你才能想起这些招式,去运用这些招式。学习与实践总是两个相辅相成、相互促进的过程。

然而,系统重构经典书籍不少,指导我们实践的书籍却不多。相信有许许多多有志之士,在看过重构的书籍以后,激情洋溢、热血澎湃,但回到现实世界中,回到实际工作中却无所适从,经过一番苦苦挣扎之后,从此作罢。因此,本书将从实践出发,用实际工作中的示例,用更加真实的视角向大家展示,系统重构是怎样指导我们工作的,让大家真正地用起来。

大话重构连载首页:http://blog.csdn.net/mooodo/article/details/32083021

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

大话重构连载8:盘点我们的重构工具箱,布布扣,bubuko.com

时间: 2024-07-31 13:03:50

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

大话重构连载首页

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

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

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

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

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

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

系统重构要求我们对代码的每一步修改,都不能改变软件的外部行为,因此在系统重构中的所有方法,都是一种代码的等量变换.重构的过程,就好像在做数学题,一步一步地进行算式的等量变换.经过一系列等量变换,最终的结果虽然在形式上与原式不一样,但通过计算可以得到与原式完全相同的结果. 这种等量变换对于重构来说非常重要,它使得我们进行重构以后,程序还是那些程序,代码还是那些代码.但是,等量变换不等于原地踏步.正如矩阵通过等量变换可以得到方程组的解,微积分可以通过等量变换计算最终的结果,重构通过等量变换,在保证代

大话重构连载4:大布局与小步快跑

以往我们在重新设计一个系统时,总是喜欢大布局.全面地整理系统需求,全面地分析系统功能,再全面地设计系统.开发.测试.这样一个过程往往会持续数月,花费大量的工作量.但是,不到最后设计出来,谁都不知道会不会存在问题.这就是“大布局”的弊病. 正因为如此,软件大师在讲述系统重构时总是强调,系统重构应当避免大设计,而应当尽量采用一个一个连续不断的小设计.这就是我们所说的“小步快跑”的设计模式. 小步快跑体现出了敏捷软件开发的特点——简单与快速反馈.不要想得太多了,活在今天的格子里,因为你永远不可能预知今

大话重构连载2:什么是系统重构

当我们开始系统重构的时候,不是着手去修改代码,而是首先建立测试机制.不论什么程序,只要是被我们修改了,理论上就可能引入BUG,因此我们就必须要进行测试.既然是测试就必须要有一个正确与否的评判标准.以往的测试,其评判的标准就是是否满足业务需求.因此,测试人员往往总是拿着需求文档测试系统. 与以往的代码修改不同,重构没有引入任何新的需求,系统原来什么功能,重构以后还是这些功能.因此,重构的测试标准就只有一个,就是与之前的功能完全保持一致,仅此而已. 然而在许多经典的重构书籍中,大师们总是建议我们首先

大话重构连载1:遗留系统——软件工业时代的痛

我常常感到幸运,我们现在所处的是一个令人振奋的时代,我们进入了软件工业时代.在这个时代里,我们进行软件开发已经不再是一个一个的小作坊,我们在进行着集团化的大规模开发.我们开发的软件不再是为某个车间.某个工序设计的辅助工具,它从某个单位走向整个集团,走向整个行业,甚至整个社会,发挥着越来越重要的作用.一套软件所起到的作用与影响有多大,已经远远超越了所有人的想象,成为一个地区.一个社会,乃至整个国家不可或缺的组成部分.慢慢地,人们已经难以想象没有某某软件或系统的生活和工作会是怎样的.这就是软件工业时

大话重构连载:遗留系统——软件工业时代的痛

我经常感到幸运,我们如今所处的是一个令人振奋的时代,我们进入了软件工业时代.在这个时代里,我们进行软件开发已经不再是一个一个的小作坊,我们在进行着集团化的大规模开发.我们开发的软件不再是为某个车间.某个工序设计的辅助工具.它从某个单位走向整个集团,走向整个行业,甚至整个社会,发挥着越来越关键的数据.一套软件所起到的作用与影响有多大.已经远远超越了全部人的想象.成为一个地区.一个社会.乃至整个国家不可或缺的组成部分. 慢慢地,人们已经难以想象没有某某软件或系统的生活和工作会是如何的.这就是软件工业

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

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