大话重构连载8:盘点我们的重构工具箱
程序员文章站
2022-03-02 13:37:24
...
下面我们来盘点一下系统重构工具箱里都有什么,也就是看一看系统重构到底都有哪些方法。系统重构总是在不同层次上调整我们的代码,因此重构方法也就分为了多个层次。从总体上看,重构方法分为以下几个层次:方法的重构、对象的重构、对象间的重构、继承体系间的重构、组织数据的重构与体系架构的重构。
前面那个例子我们可以清楚地看到方法的重构过程。方法的重构往往发生在一个对象的内部,是对一个对象内部的优化。从这个例子中,我们首先看到了增加注释、调整顺序、重命名变量、进行分段等操作,这些虽然不是什么重构方法,却是我们进行前期准备的常用技法。随后我们将通过注释分段存放的各个代码段提取出来,形成单独的函数。这种重构方法被称为“抽取方法(Extract Method)”。
随后我们继续重构。仔细观察这个程序我们发现,这个程序内聚性不好。最好用一个DateUtil管理诸如getHour()的方法,用Greeting管理各种问候语及其规则,因此我们进行了如下重构:
这里我们将getHour()从HelloWorld类中抽取出来,放到了DateUtil类中,使HelloWorld类中仅保留一个引用。同时,我们将getFirstGreeting()与getSecondGreeting()从HelloWorld类中抽取出来,放到了Greeting类中,使原程序变为引用。这样的技法我们称之为“抽取类(Extract Class)”。
再仔细观察这一段程序:
除了morning, afternoon, night以外,如果我们再增加evening,程序的可扩展性就不好了。因此我们将它们提取出GreetingRule接口,并分别编写了morning, afternoon, night和evening的实现类:
其它几个实现类我就不累牍了,最后将getSecondGreeting()方法改成这样:
这种将相似的,或者同类型的代码抽取出来形成接口,以及接口下的多个实现,我们称之为“抽取接口(Extract Interface)”。
看了这些例子你可能会有一个疑问,这样简单的程序搞成这样,是否值得?是的,不错,程序的结构应当与需求的复杂度相适应。简单的需求编写简单的程序,复杂的需求编写复杂的程序。如果将简单的需求,为了玩技术,搞成了复杂的程序,那就是“过度设计”。但这里为了更加清楚的向大家展示重构,又能够使大家不要因为复杂的程序而分心,故而将简单程序过度设计了一把。但在后面我们可以看到,当业务需求逐渐复杂时,我们进行以上这些重构是值得的。
文后附录列出了所有的重构方法,它们都来源于重构经典书籍《重构,改善既有代码的设计》,在日后的时间我们应当反复咀嚼这些方法。
正如武侠大师金庸所说的“无招胜有招”,如此多的重构方法不是要让你去生搬硬套,而是应该对其进行深刻理解以后,最终变成你自己的重构方法。我们在实际工作中不要过于介意我们用了什么重构方法,哪次重构是用的哪个方法,只要是合适的设计就OK。但是,在无招胜有招之前,我们必须要有招,即学会了、理解了各个招式,在实际工作中你才能想起这些招式,去运用这些招式。学习与实践总是两个相辅相成、相互促进的过程。
然而,系统重构经典书籍不少,指导我们实践的书籍却不多。相信有许许多多有志之士,在看过重构的书籍以后,激情洋溢、热血澎湃,但回到现实世界中,回到实际工作中却无所适从,经过一番苦苦挣扎之后,从此作罢。因此,本书将从实践出发,用实际工作中的示例,用更加真实的视角向大家展示,系统重构是怎样指导我们工作的,让大家真正地用起来。
大话重构连载首页:http://fangang.iteye.com/blog/2081995
特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!
前面那个例子我们可以清楚地看到方法的重构过程。方法的重构往往发生在一个对象的内部,是对一个对象内部的优化。从这个例子中,我们首先看到了增加注释、调整顺序、重命名变量、进行分段等操作,这些虽然不是什么重构方法,却是我们进行前期准备的常用技法。随后我们将通过注释分段存放的各个代码段提取出来,形成单独的函数。这种重构方法被称为“抽取方法(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()方法改成这样:
/** * 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://fangang.iteye.com/blog/2081995
特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!