象相合 发表于 2017-12-2 14:46

《重构-改善既有代码设计》【2-4】章整理学习分享(重构原则,junit测试)

      这篇文章整合了《重构-改善既有代码设计》2-4章的一些信息。主要有
               1. 分析整理的重构原则。
               2. 重构的手法场景介绍。
               3. Junit测试的使用方式。
      分享给大家,希望大家不要直接点收藏就关了,全部浏览一遍,可以在潜意识中酝酿这些大神们积淀多年的智慧。
      有条件的同学,可以直接下载电子书,自己看一遍原本收获更多。

         一、分析整理的重构原则。

[*]为即将修改的代码建立一组可靠的测试环境。
[*]临时变量助长冗长而复杂的函数。
[*]最好不要再另一个对象的属性基础上运用switch语句。如果要switch也要在类自己的数据上使用。
[*]事不过三,三则重构。
[*]首先写出可调的程序,然后调整它以求获得足够速度。
[*]一个好的名字可以省去读函数内容的操作。
[*]每当需要注释说明时,可以被写进一个函数里,并以用途命名,关键不在函数长度,而在函数做什么,怎么做。
[*]条件和循环也是提炼信号。
[*]针对外界变化的所有相应修改,应该只发生在单一类中,这个类的所有内容随外界变化。
[*]一个函数使用几个类的功能,它应该被放在最多被此函数使用的数据一起。
[*]对于出现在不同类/方法内的一些绑在一起的数据,应该给它们设置对象。
[*]少用switch,switch代表重复。
[*]两个帽子原则:不要边写代码边重构,带上写代码的帽子,觉得需要重构了,再带上重构的帽子去做,如此反复。
[*]注释多写为什么这样,过多注释不一定是好,尝试重构。
[*]先编写测试代码可以将关注点放在接口而非实现。
[*]每当出现一个BUG,先写一个测试单元测试BUG。不要修改以前的测试单元,因为它可能会在修复BUG后出错。
[*]测试:异常,边界
[*]测试太多带来的效益呈现递减态势。但是我们尽量测试大多数BUG。


          二、重构的手法场景介绍。【由于IDE里使用的是英文,因此了解每一个英文意义尤为重要。】


[*]Duplicated Code

[*] 同一个类两个函数有相同表达式:Extract Method(110) 提炼相同代码。
[*] 互为兄弟子类内含有相同表达式:Extract Method(110) -> Pull Up Method(332) 推入超类 ->Form Template Method(345) 获得Template Method设计模式 -> Substitute Algorithm(139)替换算法。
[*] 毫不相关的类:Extract Class(149)


[*]Long Method

[*]99%场合使用:Extract Method(110)找到适合在一起的提炼即可。
[*]有大量参数和临时变量:Replace Temp with Query(120)消除临时变量,Introduce Parameter Object(295)和Preserve Whole Object(288)可将过长参数列变简洁。还不行就用Replace Method with Method Object(135)。
[*]条件和循环时:Decompose Conditional(238)处理条件。Extract Method(110)处理循环。


[*]Large Class

[*]类太大:Extract Class(149)甚至可以Extract Subclass(330)提取子类。先确定客户端如何使用他们,再运用Extract Interface(341)为每一个使用方式提炼一个接口。这可以帮助看清如何分解类。
[*]如果是个GUI类:使用Duplicate Observed Data(189)。


[*]Long Parameter List过长参数列

[*]如果向已有对象发出一条请求就可以取代一个参数,使用Replace Parameter with Method(292)。
[*]使用Preserve Whole Object(288)将同一对象的数据收集起来,并以对象替换。
[*]缺乏合理对象归属:使用Introduce Parameter Object(295)制造参数对象。


[*]Divergent Change发散式变化

[*]一个类受多个变化的影响:Extract Class(149)


[*]Shotgun Surgery霰弹式修改

[*]每遇到一个变化,要在不同类做修改:Move Method(142)和Move Field(146)把需要修改的代码放进同一个类。使用Inline Class(154)把一系列相关行为放进一个类。


[*]Feature Envy依恋情结

[*]函数为了计算某个值,从另一个对象调用一堆取值函数:把这个使用Move Method(142)把它移到该去的地方。使用Extract Method(110)把这一部分提炼到独立函数,使用Move Method(142)放在该去的地方。如果有多个类的功能,使用Extract Method(110)分解为多个小函数,再放在调用最多数据的类里。


[*]Data Clumps数据泥团

[*]对于多个地方绑定在一起的数据:使用Extract FClass(149)将他们提炼到一个独立对象。然后转到函数签名,使用Introduce Parameter Object(295)或Preserve Whole Object(288)为他减肥。这样可以简化调用。


[*]Primitive Obsession基本类型偏执

[*]对象可以在小类型创建小对象:使用Replace Data Value with Object(175)进入对象世界。
[*]替换的数据值是类型码,并不影响行为:使用Replace Type Code with Class(218)
[*]与类型码相关的条件表达式:使用Replace Type Code with Subclass(213)或Replace Type Code wtih State/Strategy(227)处理。
[*]总被放一起的字段:使用Extract Class(149)
[*]参数列看到基本型数据:使用Introduce Parameter Object(295)。
[*]从数组中挑选数据:使用Replace Array with Object(186)


[*]Switch Statements:Switch惊悚现身

[*]使用多态代替Switch:Extract Method(110)->Move Method(142)->Replace Type Code with Subclasses(223)或Replace Type Code with State/Strategy(227)完成继承结构->Replace Conditional with Polymorphism(255)
[*]如果是单一函数的选择示例:使用Replace Parameter with Explicit Method(285)
[*]如果选择条件有null:使用Introduce Null Object(260)


[*]Parallel Inheritance Hierarchies平行继承体系

[*]每当为某各类增加子类,也要为另一个类增加子类:让继承体系的实例引用另一个继承体系的实例。再使用Move Method(142)和Move Field(146)。



[*]Lazy Class冗赘类

[*]一个类变得不再有价值:使用Collapse Hierarchy(344)和Inline Class(154)。


[*]Speculative Generality夸夸其谈未来性

[*]对于未来可能会有的方法或类:如果它没被用到,使用Collapse Hierarchy(344)或Inline Class(154)。
[*]某些参数未被使用:Remove Parameter(154)
[*]函数名称有多余抽象意味:Rename Method(273)


[*]Temporary Field令人迷惑的暂时字段

[*] 某个实例变量仅因特定情况而设:使用Extract Class(149)提取这个情况并把相关代码放进去。也可以用Introduce Null Object(260)在“变量不合法”的情况创建Null对象。
[*]复杂算法需要好几个变量:Extract Class(149)把这些变量和其他相关函数提炼到一个类作为函数对象Beck。



[*]Message Chains过度耦合的消息链

[*]一个对象请求一个对象又请求一个对象…:使用Hide Delegate(157) 可以在消息链不同位置使用。更好的做法:Extract Class(149)提炼到一个函数,Move Method(142)推入消息链。


[*]Middle Man中间人

[*]过度委托:使用Remove Middle Man(160)和负责的对象打交道。运用InlineMethod(117)放进调用端。如果Middle Man还有其他行为,使用Replace Delegation with Inheritance(355)变成实责对象子类,就可以扩展原对象行为。


[*]Inappropriate Intimacy狎昵关系

[*]两个类过去亲密:使用Move Method(142)和Move Field(146)划清界限。或使用Change Bidirectional Association to Unidirectional(200)让一个类斩断关系。Extract Class(146)提炼到一个安全地点。或使用Hide Delegate(157)


[*]Alternative Classes with Different Interfaces异曲同工的类

[*]两个函数做同一件事,却有不同签名。使用Rename Method(273)根据用途命名。使用Move Method(142)将某些行为移入类直到协议相同。使用Extract Superclass(336)可以减少重复冗余代码。


[*]Incomplete Library Class不完美的库类

[*]修改库类的函数:使用Introduce Foreign Method(162)
[*]添加额外行为:使用Introduce Local Extension(164)


[*]Data Class纯稚的数据类

[*]有一些用于访问(读写)字段的函数,其他类过分操作它们:使用Encapsulate Field(206)封装对不该被其他类修改的字段,使用Remove Setting Method(300)对这些点Move Method(142)把它们调到Data Class不能搬移整个就搬部分,不久就可以使用Hide Method(303)把它们隐藏。


[*]Refused Bequest被拒绝的遗赠

[*]继承设计错误:新建兄弟类,使用Push Down Method(328)和Push Down Field(329)把用不到的推给兄弟类。超类只持有所有子类共享的东西。
[*]子类复用了超类行为,却又不愿意支持超类接口:使用Replace Inheritance with Delegation(352)达到目的。


[*]Comments过多注释

[*]太多注释意味着坏味道:使用Extract Method(110),还需要注释的话使用Rename Method(273),如果需要注释需求使用Introduce Assertion(267)


         三、Junit测试的使用方式。

      上github:https://github.com/EleComb/Reconstruction
      如果你不知道github怎么用,看这里或官方文档

      OK,今天的分享就是这些了,祝大家学习进步!

junnyfei 发表于 2017-12-2 14:52

这么牛逼,学习了。。。

fq645122 发表于 2017-12-2 15:18

表示头大
页: [1]
查看完整版本: 《重构-改善既有代码设计》【2-4】章整理学习分享(重构原则,junit测试)