重构改善既有代码的设计--重新组织函数

重新组织函数

1、ExtractMethod(提炼函数)

动机:你有一段代码可以被组织在一起并独立出来。

做法:将这段代码放进一个独立函数中,并让函数名称解释该函数的用途。

如下面的实例,提炼后的代码变得更清晰易读。

代码1:

voidprintOwing(doublepreviousAmount)){

Enumeratione=_orders.elemnets();

doubleoutstanding=previousAmount*1.2);

//pirntbanner

System.out.println("***************************");

System.out.println("******CustomerOwes******");

System.out.println("***************************");

//calculateoutstanding

while(e.hasMoreElements()){

Ordereach=(Order)e.nextElement();

outstanding+=each.getAmout();

}

//printdetails

System.out.print("name:"+_name);

System.out.print("amout:"+outstanding);

}

代码2:

voidprintOwing(doublepreviousAmount){

printBanner();

doubleoutstanding=getOutstanding(previousAmount*1.2);

printDetails(outstanding);

}

voidprintBanner(){

System.out.println("***************************");

System.out.println("******CustomerOwes******");

System.out.println("***************************");

}

doublegetOutstanding(doubleinitialValue){

doubleresult=initalValue;

Enumeratione=_orders.elemnets();

while(e.hasMoreElements()){

Ordereach=(Order)e.nextElement();

result+=each.getAmout();

}

returnresult;

}

voidprintDetails(doubleoutstanding){

System.out.print("name:"+_name);

System.out.print("amout:"+outstanding);

}

2、InlineMethod(内涵函数)

动机:一个函数的本体与名称同样清楚易懂。

做法:在函数调用点插入函数本体,然后移除该函数。

代码1

intgetRating(){

return(moreThanFiveLateDeliveries())?2:1;

}

booleanmoreThanFiveLateDeliveries(){

return_numberOfLateDeliveries>5;

}

代码2

intgetRating(){

return(_numberOfLateDeliveries>5)?2:1;

}

3、InlineTemp(内联临时变量)

动机:你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。

做法:将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。

备注:检查该变量是否真的被赋值一次,可以将该变量声明为final,然后编译。

代码1

doublebasePrice=anOrder.basePrice();

return(basePrice>1000);

代码2

return(anOrder.basePrice()>1000);

4、ReplaceTempwithQuery(以查询取代临时变量)

动机:临时变量的问题在于:他们是暂时的,而且只能在所属函数内使用。由于临时变量只在所属函数内可见,所以他们会驱使你写更长的函数,因为只有这样你才能访问到需要的临时变量。如果把临时变量替换为一个查询,那么同一个类中的所有函数都将可以获得这份信心。这将带给你极大帮助,使你能够为这个类编写更清晰的代码。

做法:将这个表达式提炼到一个独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可被其他函数使用。

代码1

doublebasePrice=_quantity*_itemPrice;

if(basePrice>1000)

reutrnbasePrice*0.95;

else

returnbasePrice*0.98;

代码2

if(basePrice()>1000)

returnbasePrice()*0.95;

else

returnbasePrice()*0.98;

....

doublebasePrice(){

return_quantity*_itemPrice;

}

这个重构手法较为简单,如果临时变量比较多,还需要运用SplitTemporaryVariable(拆分临时变量)或SeparateQueryfromModifier(将查询函数和修改函数分离)使情况变得简单一些,然后再替换临时变量。如果你想替换的临时变量是用来收集结果的(例如循环中的累加值),就需要将某些程序逻辑(例如循环)复制到查询函数去。

代码1

doublegetPrice(){

intbasePrice=_quantity*_itemPrice;

doublediscountFactor;

if(basePrice>1000)discountFactor=0.95;

elsediscountFactor=0.98;

returnbasePrice*discountFactor;

}

代码2

doublegetPrice(){

returnbasePrice()*discountFactor();

}

privateintbasePrice(){

return_quantity*_itemPrice;

}

privatedoublediscountFactor(){

if(basePrice()>1000)return0.95;

elsereturn0.98;

}

5、IntroduceExplainingVariable(引入解释性变量)

动机:你有一个复杂的表达式。在条件逻辑中,IntroduceExplainingVariable特别有价值:你可以用这项重构将每个条件子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。使用这项重构的另一种情况是,在较长算法中,可以运用临时变量来解释,每一步运算的意义。

代码1

if((platform.toupperCase().indexOf("MAC")>-1)&&

(browser.toUpperCase().indexOf("IE")>-1)&&

wasInitialized()&&resize>0){

//dosomething

}

代码2

finalbooleanisMacOs=platform.toupperCase().indexOf("MAC")>-1;

finalbooleanisIEBrowser=browser.toUpperCase().indexOf("IE")>-1;

finalbooleanwasResized=resize>0;

if(isMacOs&&isIEBrowser&&wasResized){

//dosomething

}

类似的,我们也经常用ExtractMethod(提炼函数)来解释表达式用途,如下示例:

代码1

doubleprice(){

//priceisbaseprice-quantitydiscount+shipping

return_quantity*_itemPrice-

Math.max(0,_quantity-500)*_itemPrice*0.05+

Math.min(_quantity*_itemPrice*0.1,100.0);

}

代码2

doubleprice(){

returnbasePrice()-quantityDiscount()+shipping();

}

privatedoublequantityDiscount(){

returnMath.max(0,_quantity-500)*_itemPrice*0.05;

}

privatedoubleshipping(){

returnMath.min(basePrice()*0.1,100.0);

}

privatedoublebasePrice(){

return_quantity*_itemPrice;

}

IntroduceExplainingVariable(引入解释性变量)和ExtractMethod(提炼函数)比较:两者都可以达到解释表达式的效果,我觉得主要看临时变量是否被公用到,如果不是,则用引入解释性变量即可。

6、SplitTemporaryVariable(分解临时变量)

场景:你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于手机计算结果。

分析:这种临时变量应该制备赋值一次。如果它们被赋值超过一次,就意味它们在函数中承担了一个以上的责任。如果临时变量承担多个责任,它就应该被替换(分解)为多个临时变量,每个变量只承担一个责任。同一个临时变量承担两件不同的事情,会令代码阅读者糊涂。

代码1

doubletemp=2*(_height+_width);

System.out.println(temp);

temp=_height*_width;

System.out.println"temp);

代码2

finaldoubleperimeter=2*(_height+_width);

System.out.println(perimeter);

finaldoublearea=_height*_width;

System.out.println(area);

7、RemoveAssignmentstoParameters(移除对参数的赋值)

场景:代码对一个参数进行赋值

做法:以一个临时变量取代该参数的位置。

分析:从本质上说,参数是对象的引用,而对象的引用时按值传递的。因此我可以修改参数对象的内部状态(属性),但对参数对象重新赋值是没有意义。

代码1

intdiscount(intinputVal,intquantity,intyearToDate){

if(inputVal>50)inputVal-=2;

}

代码2

intdiscount(intinputVal,intquantity,intyearToDate){

intresult=inputVal;

if(inputVal>50)result-=2;

}

我还可以为参数加上关键词final,从而强制它遵循“不对参数赋值”这一惯例:

代码1

intdiscount(intinputVal,intquantity,intyearToDate){

if(inputVal>50)inputVal-=2;

if(quantity>100)inputVal-=1;

if(yearToDate>10000)inputVal-=4;

}

代码2

intdiscount(finalintinputVal,finalintquantity,finalintyearToDate){

intresult=inputVal;

if(inputVal>50)result-=2;

if(quantity>100)result-=1;

if(yearToDate>10000)result-=4;

}

不过我得承认,我不经常使用final来修饰参数,因为我发现,对于提高短函数的清晰度,这个方法并无太大帮助。我通常会在较长的函数中使用它,让它帮助我检查参数是否被做修改。

8、ReplaceMethodwithMethodObject(以函数对象取代函数)

动机:你有一个大型函数,其中对局部变量的使用使你无法采用ExtractMethod(提炼函数)。

做法:将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段。然后你可以在同一个对象中将这些大型函数分解为多个小型函数。

9、SubsituteAlgorithm(替换算法)

动机:你需要把某个算法替换为另一个更清晰的算法。

做法:将函数本体替换为另一个算法。

代码1

StringfoundPerson(String[]people){

for(inti=0;i<people.length;i++){

if(people[i].equals("Don")){

return"Don";

}

if(people[i].equals("John")){

return"John";

}

if(people[i].equals("Kent")){

return"Kent";

}

}

return"";

}

代码2

StringfoundPerson(String[]people){

Listcandidates=Arrays.asList(newString[]{"Don","John","Kent");

for(inti=0;i<people.length;i++){

if(candidates.contains(people[i])){

returnpeople[i];

}

}

return"";

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一直很喜欢重构这本书,但是由于自己记性不太好,书看过之后其中的方法总是记不住,于是想如果有电子版的重构书就好了,工作中遇到重构的问题可以随时打开查阅。在网上搜索了许久,发现重构这本书有英文chm版本的,而中文版的电子书只有扫描的PDF版本,用起来非常不方便。于是萌生想做一本重构工具书的想法,本来打算自己重新重构书的内容再整理归类一下,后来发现原书的目录编排就很适合做工具书,包括坏味道分类,重构手法归类等,都有了一个比较系统的整理。因此,我利用空余时间制作了这样的一本中文的chm版重构,希望对大家有所帮助,也算对中国软件业做出一点小小的贡献。 本书基本上是取自”重构”中文版一书的内容,但格式上参照的是chm英文版的格式,还有一些格式小修改,比如第一章的重构前后代码对比。因为时间匆促,个人能力有限,本书难免存在一些缺漏,如果大家发现有问题,随时可以给我发邮件,我会尽快更新错误的内容。 最后再次感谢几位大师 Martin Fowler、Kent Beck等,还有翻译的侯捷和熊节先生,为我们带来这么精彩的一本书。谢谢。 免责声明:本书仅供个人学习研究之用,不得用于任何商业目的,不得以任何方式修改本作品,基于此产生的法律责任本人不承担任何连带责任。
第1章 重构,第一个案例1 1.1 起点1 1.2 重构的第一步7 1.3 分解并重组statement()8 1.4 运用多态取代与价格相关的条件逻辑34 1.5 结语52 第2章 重构原则53 2.1 何谓重构53 2.2 为何重构55 2.3 何时重构57 2.4 怎么对经理说60 2.5 重构的难题62 2.6 重构设计66 2.7 重构与性能69 2.8 重构起源何处71 第3章 代码的坏味道75 3.1 DuplicatedCode(重复代码)76 3.2 LongMethod(过长函数)76 3.3 LargeClass(过大的类)78 3.4 LongParameterList(过长参数列)78 3.5 DivergentChange(发散式变化)79 3.6 ShotgunSurgery(霰弹式修改)80 3.7 FeatureEnvy(依恋情结)80 3.8 DataClumps(数据泥团)81 3.9 PrimitiveObsession(基本类型偏执)81 3.10 SwitchStatements(switch惊悚现身)82 3.11 ParallelInheritanceHierarchies(平行继承体系)83 3.12 LazyClass(冗赘类)83 3.13 SpeculativeGenerality(夸夸其谈未来性)83 3.14 TemporaryField(令人迷惑的暂时字段)84 3.15 MessageChains(过度耦合的消息链)84 3.16 MiddleMan(中间人)85 3.17 InappropriateIntimacy(狎昵关系)85 3.18 AlternativeClasseswithDifferentInterfaces(异曲同工的类)85 3.19 IncompleteLibraryClass(不完美的库类)86 3.20 DataClass(纯稚的数据类)86 3.21 RefusedBequest(被拒绝的遗赠)87 3.22 Comments(过多的注释)87 第4章 构筑测试体系89 4.1 自测试代码的价值89 4.2 JUnit测试框架91 4.3 添加更多测试97 第5章 重构列表103 5.1 重构的记录格式103 5.2 寻找引用点105 5.3 这些重构手法有多成熟106 第6章 重新组织函数109 6.1 ExtractMethod(提炼函数)110 6.2 InlineMethod(内联函数)117 6.3 InlineTemp(内联临时变量)119 6.4 ReplaceTempwithQuery(以查询取代临时变量)120 6.5 IntroduceExplainingVariable(引入解释性变量)124 6.6 SplitTemporaryVariable(分解临时变量)128 6.7 RemoveAssignmentstoParameters(移除对参数的赋值)131 6.8 ReplaceMethodwithMethodObject(以函数对象取代函数)135 6.9 SubstituteAlgorithm(替换算法)139 第7章 在对象之间搬移特性141 7.1 MoveMethod(搬移函数)142 7.2 MoveField(搬移字段)146 7.3 ExtractClass(提炼类)149 7.4 InlineClass(将类内联化)154 7.5 HideDelegate(隐藏“委托关系”)157 7.6 RemoveMiddleMan(移除中间人)160 7.7 IntroduceForeignMethod(引入外加函数)162 7.8 IntroduceLocalExtension(引入本地扩展)164 第8章 重新组织数据169 8.1 SelfEncapsulateField(自封装字段)171 8.2 ReplaceDataValuewithObject(以对象取代数据值)175 8.3 ChangeValuetoReference(将值对象改为引用对象)179 8.4 ChangeReferencetoValue(将引用对象改为值对象)183 8.5 ReplaceArraywithObject(以对象取代数组)186 8.6 DuplicateObservedData(复制“被监视数据”)189 8.7 ChangeUnidirectionalAssociationtoBidirectional(将单向关联改为双向关联)197 8.8 ChangeBidirectionalAssociationtoUnidirectional(将双向关联改为单向关联)200 8.9 ReplaceMagicNumberwithSymbolicConstant(以字面常量取代魔法数)204 8.10 EncapsulateField(封装字段)206 8.11 EncapsulateCollection(封装集合)208 8.12 ReplaceRecordwithDataClass(以数据类取代记录)217 8.13 ReplaceTypeCodewithClass(以类取代类型码)218 8.14 ReplaceTypeCodewithSubclasses(以子类取代类型码)223 8.15 ReplaceTypeCodewithState/Strategy(以State/Strategy取代类型码)227 8.16 ReplaceSubclasswithFields(以字段取代子类)232 第9章 简化条件表达式237 9.1 DecomposeConditional(分解条件表达式)238 9.2 ConsolidateConditionalExpression(合并条件表达式)240 9.3 ConsolidateDuplicateConditionalFragments(合并重复的条件片段)243 9.4 RemoveControlFlag(移除控制标记)245 9.5 ReplaceNestedConditionalwithGuardClauses(以卫语句取代嵌套条件表达式)250 9.6 ReplaceConditionalwithPolymorphism(以多态取代条件表达式)255 9.7 IntroduceNullObject(引入Null对象)260 9.8 IntroduceAssertion(引入断言)267 第10章 简化函数调用271 10.1 RenameMethod(函数改名)273 10.2 AddParameter(添加参数)275 10.3 RemoveParameter(移除参数)277 10.4 SeparateQueryfromModifier(将查询函数和修改函数分离)279 10.5 ParameterizeMethod(令函数携带参数)283 10.6 ReplaceParameterwithExplicitMethods(以明确函数取代参数)285 10.7 PreserveWholeObject(保持对象完整)288 10.8 ReplaceParameterwithMethods(以函数取代参数)292 10.9 IntroduceParameterObject(引入参数对象)295 10.10 RemoveSettingMethod(移除设值函数)300 10.11 HideMethod(隐藏函数)303 10.12 ReplaceConstructorwithFactoryMethod(以工厂函数取代构造函数)304 10.13 EncapsulateDowncast(封装向下转型)308 10.14 ReplaceErrorCodewithException(以异常取代错误码)310 10.15 ReplaceExceptionwithTest(以测试取代异常)315 第11章 处理概括关系319 11.1 PullUpField(字段上移)320 11.2 PullUpMethod(函数上移)322 11.3 PullUpConstructorBody(构造函数本体上移)325 11.4 PushDownMethod(函数下移)328 11.5 PushDownField(字段下移)329 11.6 ExtractSubclass(提炼子类)330 …… 第12章 大型重构359 第13章 重构,复用与现实379 第14章 重构工具401 第15章 总结409 参考书目413 要点列表417 索引419

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值