《重构》第三章学习笔记
我们必须培养自己的判断力,来决定在什么时候进行重构。
1.1 DuplicateCode(重复代码)
如果你在一个以上地点看到相同的程序结构,那么将他们合而为一会更好。
1.2 LongMethod(过长函数)
拥有短函数的对象会活得比较好,比较长。
间接层所能带来的全部益处:解释能力(可读性),共享能力(重用性),选择能力(?)。
现在OO 语言基本解决了函数调用所产生的开销。
“ 你应该更积极进去的分解函数。我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个函数中,并以其用途(而非实现手法)命名。我们可以对一组甚至短短一行代码(拥有复杂逻辑,难以理解)做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫的这么做。关键不在于函数的长度,而在于“做什么”和“如何做”之间的语义距离。 ”
“如何确定该提炼哪一段代码?一个很好的技巧是:寻找注释。它们通常是指出“代码用途和实现手法间的语义距离”的信号。如果代码需要用注释来说明其用途,那么就要考虑把这段代码提炼成独立的函数,并且用注释来为此函数命名。”
复杂条件式和循环液常常是提炼的信号。
1.3 LargeClass(过大类)
如果想利用单一的class 做太多的事情,其内往往会出现太多的 instance 变量。
如果class 中拥有太多的代码,也是“代码重复、混乱、死亡”的绝佳滋生点。
1.4 LongParameterList(过长的参数列表)
过长的产生导致程序难以理解。
1.5 DivergentChange(发散式变化)
“ 一个class 受多个外界变化的影响 ”,则把这多个变化封装成一个新的类。即“ 将总是一起变化的东西放在一起 ”
针对外界某一变化所有相应的修改,都应该只发生在单一的class 中,而这个 class 的所有内容都应该反映该外界变化。总的思想就是,封装变化。这个地方和设计模式的想法是一致的。
1.6 ShotgunSurgery(散弹式修改)
和发散式变化不同,每次遇到变化,都要在多个class 中进行小的修改以响应之。他们分散在多处,很容易出错。
这里的主要思想是集中变化。
散弹式修改指的是,“ 一种变化引发多个class 的修改 ”,发散式变化指的是“ 一个class 受多个外界变化的影响 ”。
这两种情况下,通过重构, 使“外界变化”和“待修改类”呈一对一关系 的理想境地。
1.7 FeatureEnvy(依恋情节)
某个函数对其他类的数据的兴趣,高过对hostclass 的兴趣。即对其他的类的数据的依赖十分大。
1.8 DataClumps(数据泥团)
数据泥团指的是总是绑定在一起出现的数据。
一个好的评断方法:删除众多数据中的一项数据,其他数据是否是因而失去了意义?如果他们不再有意义:你应该为他们产生一个新的对象。
形成新的对象后,可以根据FeatureEnvy 将一些操作移至此对象中。
1.9 PrimitiveObsession(基本型别偏执)
建立多个很小,但是很灵活的对象。
1.10 SwitchStatements( switch 惊悚现身)
使用面向对象编程,要少用switch 和 case 语句。而是用多态来替换它。
1.11 ParallelInheritanceHierarchies(平行继承体系)
每当你为一个class 增加一个 subclass 的时候,必须为另一个 class 增加一个 subclass 。一般这两个 class 的前缀相同。
1.12 LazyClass(冗赘类)
类显得多余,没有价值。
1.13 SpeculativeGenerality(夸夸其谈未来性)
这个往往是过度设计的结果:对某种变化的应对,而这种变化没有发生。
1.14 TemporaryField(令人迷惑的暂时值域)
变量只在特定的情形下有效,而并不是所有的情况下有效。很多情况下,这些值域应该不属于此class ,而应该单独的提取成新的类。
1.15 MessageChains(过度耦合的消息链)
用户向一个对象索取另一个对象,然后在向后者索求另一个对象,然后在索求另一个对象——客户与查找过程的航行结构紧密耦合。
1.16 MiddleMan(中间转手人)
对象的基本特征之一就是封装——对外部世界隐藏实现细节——封装往往伴随委托。委托的过度运行,就导致了MiddleMan 。
1.17 InappropriateIntimacy(亲密关系)
两个class 之间的关系过于亲密。比如,花大量的时间探究彼此的 private 成分。
1.18 AlternativeClasseswithDifferentInterface(异曲同工的类)
类名不同,但是功能相似。
1.19 IncompleteLibraryClass(不完美的程序类库)
基础类库无法满足实际的需求。
1.20 DataClass(纯稚的数据类)
它们拥有一些值域,以及用于访问(读写)这些值域的函数,除此之外一无长物。
1.21 RefusedBequest(被拒绝的遗赠)
子类不像继承父类的函数和数据,这往往是继承体系的错误。
如果子类复用父类的行为,但又不愿支持父类的接口,这种情况下RefusedBequest 的坏味道会很强烈。
1.22 Comments(过多的注释)
注释其实是一种香味,更多的情况下它被用作除臭剂:即代码中出现坏味道(设计糟糕的代码),然后用注释“除臭”。这个时候我们应该对这些坏味道的代码进行重构,然后,你会发现注释变成了多余的。
当你感觉需要注释,请先尝试重构,试着让所有的注释都变得多余——代码本身就是自注释的。
注释可以用来记述“为什么做某事”、“打算做某事”、“无十足把握的区域”,而不必记录“怎么做”。
2 构筑测试体系
如果你想进行重构,首要前提就是要拥有一个可靠的测试环境。
“编写优良的测试程序,可以极大的提高我的编程速度,即使不进行重构也是如此。”
2.1 自我测试代码(Self-testingCode )的价值
“Class 应该包含他们自己的测试代码。”
“每个Class 都有一个测试函数,并用它测试自己这个 Class 。”
确保所有的测试都完全自动化,让它们检查自己的测试结果。
只要写好一点功能,就立即添加测试。
一整组(asuiteof )测试就是一个强大的“臭虫”侦测器,能够大大缩减查找“臭虫”所需要的时间。
“实际上,编写测试代码的最有用时机是在开始编程之前。当你需要添加特性的时候,先写相应的测试代码。听起来离经叛道,其实不然。填写测试代码其实就是问自己:添加这个功能需要做什么。编写测试代码还能使你把注意力集中于接口而非实现上头(永远是件好事)。预先写好的测试代码也为你的工作按上一个明确的结束标志:一旦测试代码运行正常,工作就可以结束了。”
2404

被折叠的 条评论
为什么被折叠?



