豆瓣:重构:改善既有代码的设计
合而为一。
同一个类的两个函数类似,那就提取函数。两个互为兄弟的子类包含相同表达式,那就提取函数,对提取的代码推入超类中。如果代码之间只是类似,非完全相同,那么就得提取函数,建立模板了。
如果两个毫不相关的类有重复代码,考虑对其中一个提取类。
分解函数。
如果函数内含有大量的参数和临时变量,那提取函数就不管用了,这是要用查询代替临时变量,引入参数对象和保留全部对象都可以缩短过长的参数,最终的杀手锏是用方法对象代替方法。
提取哪一段代码呢?最好寻找注释吧,注释标出了代码用途和实现手法之间的语义距离。
条件表达式的提炼:分解条件。
循环的提炼:将循环和其内的代码提炼到独立函数中。
提炼类:将相关的变量一起提炼到新类中。如果类中的数个变量有着相同的前缀或字尾,提取到一个组件中。如果这个组件适合作为一个子类,那么提取子类吧。提炼类和提炼子类可以多次使用。在提炼之前,先确定客户端如何使用它们。
如果向已有对象发送一条请求就可以取代一个参数,那么用方法来代替参数。这里的已有对象可以是函数所属类的一个字段,也可能是另一参数。也可以将来自同一对象的一堆数据收集起来,以该对象替换它们。
如果某些数据缺乏合理的对象归属,那么就为它们创造一个“参数对象”。
如果某个类经常因为不同的原因在不同的方向上发生变化,这就是发散式变化。那么应该找出某特定原因而造成的所有变化,将它们提取到一个类中。
与发散式变化相反。如果每遇到某种变化,都必须在许多不同的类中作出许多小修改。这就是散弹式修改。这时候要移动方法和移动成员变量把修改放进同一类中。如果没有合适的类可以安置这些代码,那就创造一个。通常运行内联类可以把一系列的相关行为放进同一类中。
发散式变化是指“一个类受多种变化影响”,散弹式修改指“一种变化引发多个类的修改”
函数对某个类的兴趣高于对自己所处的类的兴趣,那就移动函数吧。如果只是函数中的一部分代码,那就提取代码到新函数中,再移动新函数。
如果一个函数用到几个类的功能,就把它移动到拥有最多被使用的数据的类中。或者在此之前,你可以分解一下这个函数。总要将变化的东西放在一块儿。
那些总是绑在一起的数据,应该有属于它们的对象。
尝试用一些小对象代替数据值和类型码。
用多态代替它,这时就要建立新的对象。或者,用explicit方法代替参数。如果你的选择条件之一是null,那就引入null对象。
每当你增加一个子类,也必须为另一个类增加子类。有两个继承体系。解决策略:让一个继承体系的实例引用另一继承体系的实例。如果再接再厉使用移动方法和移动成员变量,那就更有效果。
你所创建的每个类,都得要被理解。如果某些子类没有做足够的工作,那就 Collapse Hierarchy。对于几乎没用的组件,用内部类。
如果用不到,就不值得做。
将临时字段提取和函数类,或者引入null对象。
解决方法:Hide Delegate。 先观察消息链的最终对象用来做什么的,看能否提取方法。
过渡使用委托,应该移除中间人,或者 Replace Delegation with Inheritance。
拆散这种关系:移动方法和成员变量。或者提炼共同点到类。
移动函数,提取类
如果你只想修改类库的一两个函数,那就引入外部函数。如果你想要添加一大堆额外行为,那就引入local extension。
数据类是指拥有一些字段,以及用于访问这些字段的函数,除此之外,什么都没有。不会说话。 Encapsulate Field封装它的数据成员。
子类应该继承超类的函数和数据,但如果它们不想或不需要继承,又该怎么办呢?
不要胡乱修改继承体系,应该应用Replace Inheritance with Delegation。
[[Comment in Code]]
当你感觉需要些注释时,先尝试重构,试着让所有的注释变得多余。
如果你需要注释来解释一块代码,尝试着提取方法,如果函数已经提取出来,但还是要注释来解释,那就试着remove method。如果你需要注释来说明某些系统的需求规格,试试Introduce Assertion。
每个类都应该有一个测试函数,并以它来测试自己这个类。
确保所有测试都完全自动化,让它们检查自己的测试结果。
撰写测试代码的最有用的时机是在开始编程之前。编写测试代码就是在问自己:添加这个功能需要做些什么。
频繁地运行测试,每次编译请把测试也考虑进去,每天至少执行每个测试一次。
编写未臻完善的测试并实际运行,好过对完美测试的无尽期待。
考虑可能出错的边界条件,把测试火力集中在那儿。
当事情被人误应该会出错时,别忘了是否抛出了预期的异常。
不要因为测试无法捕捉所有的 bug 就不写测试,因为测试的确可以捕捉到大多数bug-----花合理时间捕捉大部分 bug。
