Skip to content

Latest commit

 

History

History
165 lines (96 loc) · 7.13 KB

File metadata and controls

165 lines (96 loc) · 7.13 KB

重构

豆瓣:重构:改善既有代码的设计

cover

2. 代码的坏味道

重复代码

合而为一。

同一个类的两个函数类似,那就提取函数。两个互为兄弟的子类包含相同表达式,那就提取函数,对提取的代码推入超类中。如果代码之间只是类似,非完全相同,那么就得提取函数,建立模板了。

如果两个毫不相关的类有重复代码,考虑对其中一个提取类。

过长函数

分解函数。

如果函数内含有大量的参数和临时变量,那提取函数就不管用了,这是要用查询代替临时变量,引入参数对象和保留全部对象都可以缩短过长的参数,最终的杀手锏是用方法对象代替方法。

提取哪一段代码呢?最好寻找注释吧,注释标出了代码用途和实现手法之间的语义距离。

条件表达式的提炼:分解条件。

循环的提炼:将循环和其内的代码提炼到独立函数中。

过大的类

提炼类:将相关的变量一起提炼到新类中。如果类中的数个变量有着相同的前缀或字尾,提取到一个组件中。如果这个组件适合作为一个子类,那么提取子类吧。提炼类和提炼子类可以多次使用。在提炼之前,先确定客户端如何使用它们。

过长参数列

如果向已有对象发送一条请求就可以取代一个参数,那么用方法来代替参数。这里的已有对象可以是函数所属类的一个字段,也可能是另一参数。也可以将来自同一对象的一堆数据收集起来,以该对象替换它们。

如果某些数据缺乏合理的对象归属,那么就为它们创造一个“参数对象”。

发散式变化

如果某个类经常因为不同的原因在不同的方向上发生变化,这就是发散式变化。那么应该找出某特定原因而造成的所有变化,将它们提取到一个类中。

散弹式修改

与发散式变化相反。如果每遇到某种变化,都必须在许多不同的类中作出许多小修改。这就是散弹式修改。这时候要移动方法和移动成员变量把修改放进同一类中。如果没有合适的类可以安置这些代码,那就创造一个。通常运行内联类可以把一系列的相关行为放进同一类中。

发散式变化是指“一个类受多种变化影响”,散弹式修改指“一种变化引发多个类的修改”

依恋情结

函数对某个类的兴趣高于对自己所处的类的兴趣,那就移动函数吧。如果只是函数中的一部分代码,那就提取代码到新函数中,再移动新函数。

如果一个函数用到几个类的功能,就把它移动到拥有最多被使用的数据的类中。或者在此之前,你可以分解一下这个函数。总要将变化的东西放在一块儿。

数据泥团

那些总是绑在一起的数据,应该有属于它们的对象。

基本类型偏执

尝试用一些小对象代替数据值和类型码。

Switch惊悚现身

用多态代替它,这时就要建立新的对象。或者,用explicit方法代替参数。如果你的选择条件之一是null,那就引入null对象。

平行继承体系

每当你增加一个子类,也必须为另一个类增加子类。有两个继承体系。解决策略:让一个继承体系的实例引用另一继承体系的实例。如果再接再厉使用移动方法和移动成员变量,那就更有效果。

冗赘类

你所创建的每个类,都得要被理解。如果某些子类没有做足够的工作,那就 Collapse Hierarchy。对于几乎没用的组件,用内部类。

夸夸其谈未来性

如果用不到,就不值得做。

令人迷惑的临时字段

将临时字段提取和函数类,或者引入null对象。

过渡耦合的消息链

解决方法:Hide Delegate。 先观察消息链的最终对象用来做什么的,看能否提取方法。

中间人

过渡使用委托,应该移除中间人,或者 Replace Delegation with Inheritance。

Inappropriate Intimacy

拆散这种关系:移动方法和成员变量。或者提炼共同点到类。

异曲同工的类

移动函数,提取类

不完美的类库

如果你只想修改类库的一两个函数,那就引入外部函数。如果你想要添加一大堆额外行为,那就引入local extension。

纯稚的数据类

数据类是指拥有一些字段,以及用于访问这些字段的函数,除此之外,什么都没有。不会说话。 Encapsulate Field封装它的数据成员。

被拒绝的馈赠

子类应该继承超类的函数和数据,但如果它们不想或不需要继承,又该怎么办呢?

不要胡乱修改继承体系,应该应用Replace Inheritance with Delegation。

过多的注释

[[Comment in Code]]

当你感觉需要些注释时,先尝试重构,试着让所有的注释变得多余。

如果你需要注释来解释一块代码,尝试着提取方法,如果函数已经提取出来,但还是要注释来解释,那就试着remove method。如果你需要注释来说明某些系统的需求规格,试试Introduce Assertion。

3. 构筑测试体系

每个类都应该有一个测试函数,并以它来测试自己这个类。

确保所有测试都完全自动化,让它们检查自己的测试结果。

撰写测试代码的最有用的时机是在开始编程之前。编写测试代码就是在问自己:添加这个功能需要做些什么

频繁地运行测试,每次编译请把测试也考虑进去,每天至少执行每个测试一次。

编写未臻完善的测试并实际运行,好过对完美测试的无尽期待。

考虑可能出错的边界条件,把测试火力集中在那儿。

当事情被人误应该会出错时,别忘了是否抛出了预期的异常。

不要因为测试无法捕捉所有的 bug 就不写测试,因为测试的确可以捕捉到大多数bug-----花合理时间捕捉大部分 bug。