重构-改善既有代码设计
1.所谓重构-是这样一个过程---在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。
重构是一种经过千锤百炼形成的有条不紊的程序整理方法,可以最大限度的减少整理过程中引入错误的几率。
本质上说,重构就是代码写好之后改进它的设计。
2.重构技术就是以微小的步伐修改程序。如果出错,很容易就能发现。
3.任何一个傻瓜都可以写出计算机可以的理解的代码。唯有写出人类容易理解的代码,才是优秀程序员。
- 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
5.重构的目的是使软件更容易被理解和修改。
6.为何重构? 重构改进软件设计。 重构是软件更容易理解。重构帮助找到bug。重构提高编程速度。
7.何时重构? 随时随地重构。
8.三次法则: 第一次做某事,只管去做 第二次做类似的事 会反感。 第三次就应该重构。
9.添加功能时重构,修补错误时重构。复审代码时重构。
10.当你感觉需要写注释时,先尝试重构,试着让所有注释都变得多余。
11.重构记录格式: 名称(),动机(为什么需要这个重构,什么情况下不该使用这个重构),做法(如何进行重构),范例。
12.提炼函数: 将一段代码放进一个独立函数中,并让函数名称解释该函数的用途。动机:函数粒度很小,被复用的机会就越大。
13.内联函数 : 在函数调用点插入函数本体,然后移除该函数。(这些函数的内部代码和名称一样可读。)
14.将表达式提炼到一个独立函数中,将这个临时变量的所有引用点替换为对新函数的调用。
15.引入解释型变量: 将复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。
16.替换算法: 将函数本体替换为另一个算法。
在对象之间搬移特性
17.搬移函数:在函数最常引用的类中建立一个有着类似行为的新函数,将就函数变成一个单纯的委托函数。或是将旧函数完全移除。
18.搬移字段: 在目标类新建一个字段,修改源字段的所有用户,令他们改用新字段。
19.提炼类: (某个类做了应该由两个类做的事 ) 建立一个新类,将相关的字段和函数从旧类搬移到新类。
20.将类内联化:(某个类没有做太多的事情) 将这个类的所有特性搬移到另一个类中,然后移除原类。
21.隐藏委托关系:(客户通过一个委托类来调用另一个对象)在服务类上建立客户所需的所有函数,用以隐藏委托关系。
22.移除中间人:(某个类做了过多的简单委托动作):让客户直接调用受托类。
23.引入外加函数:(你需要为提供服务的类增加一个函数,但你无法修改这个类)在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
24.引入本地扩展:(建立一个新类,使它包含这些额外函数,让这个扩展品成为源类的子类或包装类)
重新组织数据
25.自封装字段: (直接访问一个字段,但与字段之间的耦合关系变得笨拙)为这个字段建立取值/设值函数,并且只以这些函数来访问字段。
26.以对象取代数据值:(你的数据项需要与其他数据或行为一起使用才有意义,这时候可以用对象封装)将数据项变成对象。
27.将值对象改为引用对象:(你从一个类衍生出许多彼此相等的实例,希望他们替换为同一对象)将这个值对象变成引用对象。
28.将引用对象改为值对象:(有一个引用对象,很小且不可变,而且不易管理)将它变成一个值对象。
29.以对象取代数组:(有一个数组,其中的元素各自代表不同的东西)以对象替换数组,对于数组中的每个元素,以一个字段来表示。
30.复制“被监视数据”:(有一些领域数据置身于GUI空间中,而领域函数需要访问这些数据)将该数据复制到一个领域对象中,建立一个Observe模式,用以同步领域对象和GUI对象内的重复数据。
31.将单向关联改为双向关联:(两个类都需要使用对方特性,但其间只有一条单向连接)添加一个反向指针,并使修改函数能够同时更新两条连接。
32.将双向关联改为单向关联:(两个类之间有双向关联,但其中一个类不再需要另一个类的特性)去除不必要的关联。
33.以字面常量取代魔法数:(有一个字面数值,带有特别含义)创造一个常量,根据其意义命名,并将字面数值替换为这个常量。
34.封装字段:(你的类中存在一个public字段)将它声明为private,并提供相应的访问函数。
35.封装集合:(有个函数返回一个集合)让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。
36.以数据类取代记录:(你需要面对传统编程环境中的记录结构)为该记录创建一个“哑”的数据对象。
37.以类取代类型码:(类 之中有一个数值类型码,但它并不影响类的行为)以一个新的类替换该数值类型码。
38.以子类取代类型码:(有一个不可变得类型码,它会影响类的行为)以子类取代这个类型码。
39.以State/Strategy取代类型码:(有一个类型码,会影响类的行为,但无法通过继承手法消除它)以状态对象取代类型码。
40.以字段取代子类:(你的各个子类的唯一差别只在“返回常量数据”的函数身上)修改这些函数,使他们但会超类中的某个(新增)字段,然后销毁子类。
简化条件表达式
41.分解条件表达式:(有一个复杂的if-then-else语句)从if,then,else三个段落中分别提炼出独立函数。
42.合并条件表达式:(有一系列条件测试,都得到相同的结果)将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立函数。
43.合并重复的条件片段:(在条件表达式的每个分支上有着相同的一段代码)将这段重复代码搬移到条件表达式之外。
44.移除控制标记:(在一系列布尔表达式中,某个变量带有控制标记的作用)以break或return语句取代控制标记。
45.以卫语句取代嵌套条件表达式:(函数中的条件逻辑使人难以看清正常的执行路径)使用卫语句表现所有特殊情况。
46.以多态取代条件表达式:(有个条件表达式根据对象类型的不同,选择不同的行为)将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。
47.引入NULL对象:(你需要再三检查某对象是否为null)将null值替换为null对象。
简化函数调用
- 函数改名:(函数名未能说明函数的用途)修改函数名称。
- 添加参数:(某个函数需要从调用端得到更多信息)为此函数添加一个对象参数,让该对象带进函数所需信息。
50.移除参数:(函数本体不再需要某个参数)将参数除去。
51.将查询函数和修改函数分离:(某个函数既返回对象状态值,又修改对象状态)建立两个不同的函数,其中一个负责查询,另一个负责修改。
52.令函数携带参数:(若干函数做了类似的工作,但在函数本体中却包含了不同的值)建立单一函数,以参数表达那些不同的值。
53.以明确函数取代参数:(有一个函数,其中完全取决于参数值而采取不同的行为)针对该参数的每一个可能值,建立一个独立函数。
54.保持对象完整:(从某个对象中取出若干值,将他们作为某一次函数调用时的参数)改为传递整个对象。
55.以函数取代参数:(对象调用某个函数,并将所得结果作为参数,传递给另一个函数,而接受该参数的函数本身也能够调用前一个函数)让参数接受者去除该项参数,并直接调用前一个函数。
56.引入参数对象:(某些参数总是很自然的同时出现)以一个对象取代这些参数。
57.移除设值函数:(类中的某个字段应该在对象创建时被设值,然后就不再改变)去掉该字段的所有设值函数。
58.隐藏函数:(一个函数,从来没有被其他任何类用到)将这个函数修改为private。
59.以工厂函数取代构造函数:(你希望在创建对象时,不仅仅是做简单的构建动作)将构造函数替换为工厂函数。
60.封装向下转型:(某个函数返回的对象,需要由函数调用者执行向下转型)将向下转型动作移动到函数中。
61.以异常取代错误码:(某个函数返回一个特定的代码,用以表示某种错误情况。)改用异常。
62.以测试取代异常:(面对一个调用者可以预先检查的条件,你抛出了一个异常)修改调用者,使它在调用函数之前先做检查。
处理概括关系
63.字段上移:(两个子类拥有相同的字段)将该字段移至超类。
64.函数上移:(有些函数,在各个子类中产生完全相同的结果)将该函数移至超类。
65.构造函数本体上移:(在各个子类中拥有一些构造函数,它们的本体几乎完全一致)在超类中新建一个构造函数,并在子类构造函数中调用它。
66.函数下移:(超类中的某个函数只与部分(而非全部)子类有关)将这个函数移到相关的那些子类去。
67.字段下移:(超类中的某个字段只被部分(而非全部)子类用到)将这个字段移到需要它的那些子类中去。
68.提炼子类:(类中的某些特性只被某些(而非全部)实例用到)新建一个子类,将上面所说的那一部分特性移到子类中。
69.提炼超类:(两个类有相似特性)为这两个类建立一个超类,将相同特性移至超类。
70.提炼接口:(若干客户使用类接口中的同一子集,或者两个类的接口有部分相同)将相同的子集提炼到一个独立接口中。
71折叠继承体系:(超类和子类之间无太大区别)将他们合为一体。
72.塑造模板函数:(有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同)将这些操作分别放进独立函数中,并保持他们都有相同的签名,于是原函数也就变得相同了,然后将原函数上移至超类。
73.以继承取代委托:(你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数)让委托类继承受托类。
大型重构(四个大型重构)
74.梳理并分解继承体系:(某个继承体系同时承担两项责任)建立两个继承体系,并通过委托关系让其中一个可以调用另一个。
75.将过程化设计转为对象设计:(手上有一些传统过程化风格的代码)将数据记录变成对象,将大块的行为分成小块,并将行为移入相关对象之中。
76.将领域和表述/显示分离:(某些GUI类之中包含了领域逻辑)将领域逻辑分离出来,为他们建立独立的领域类。
77.提炼继承体系:(某个类做了太多工作,其中一部分工作是以大量条件表达式完成的)建立继承体系,以一个子类表示一种特殊情况。