《重构》(笔记二)

来源:互联网 发布:分水岭算法基本原理 编辑:程序博客网 时间:2024/05/17 00:06

第六章 重新组织函数

6.1.Extract Method(提炼函数)(110)

你有一段代码可以被组织再一起并独立出来。

将这段代码放进一个独立函数中,并让函数名解释该函数的用途。

动机:简短而良好命名的函数

做法:创建一个新函数(“做什么来命名”)。

将提炼出来的代码从原函数复制到新建的目标函数中。

仔细检查提炼出的代码,检查“作用域限于原函数”的变量。

检查是否有局部变量的值被它改变->是否可以被提炼代码段处理未一个查询,并将结果赋给相关变量。

将被提炼代码段中需要读取的局部变量,当做参数传给目标函数。

处理完所有局部变量之后,进行编译。在源函数中,将被提炼代码段替换为对目标函数的调用。


6.2.Inline Method(内联函数)(117)

一个函数的本体与名称同样清楚易懂。

在函数调用点插入函数本体,然后移除该函数。

动机:其内部代码和函数名称同样清晰易读,去掉这个函数,直接使用其中的代码。

做法:检查函数,确定它不具多态性。(如果子类继承了这个函数,就不要将此函数内联,因为子类无法覆写一个根本不存在的函数。)

找出这个函数的所有被调用点,将这个函数的所有被调用点都替换为函数本体。

编译,测试。删除该函数的定义。


6.3、Inline Temp(内联临时变量)(119)

你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。

将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。(将变量换为函数)

动机:发现某个临时变量赋予某个函数调用的返回值。

做法:检查给临时变量赋值的语句,确保等号右边的表达式没有副作用。

如果这个临时变量并未被声明未final,那就将它声明为final,然后编译。

找到该临时变量的所有引用点,将它们替换为“为临时变量赋值”的表达式。每次修改后,编译并测试。

修改完所有引用点之后,删除该临时变量的声明和赋值语句。编译,测试。


6.4.Replace Temp with Query(以查询取代临时变量)(120)

你的程序以一个临时变量保存某一表达式的运算结果。

将这个表达式提炼到一个独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可被其他函数调用。

动机:临时变量是暂时的,只能在所属函数内使用。如果把临时变量替换为一个查询,那么同一个类中的所有函数都将可以获得这份信息

做法:找出只被赋值一次的临时变量。将该临时变量声明为final。编译。

将“对该临时变量赋值”之语句的等号右侧部分提炼到一个独立函数中。编译测试。

在该临时变量身上实施Inline Temp(119)


6.5.Introduce Explaining Variable(引入解释性变量)(124)

你有一个复杂的表达式。

将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。

动机:将复杂表达式分解为比较容易管理的形式

做法:声明一个final临时变量,将待分解之复杂表达式中的一部分动作的运算结果赋值给它。

将表达式中的“运算结果”这一部分,替换为上述临时变量。

编译,测试。


6.6.Split Temporay Variable(分解临时变量)(128)

你的程序有某个历史变量被赋值超过一次,它既不是循环变量,也不被用于计算结果。

针对每次赋值,创造一个独立、对应的临时变量。

动机:如果临时变量承担多个责任,它就应该被替换(分解)为多个临时变量。

做法:将新的临时变量声明为final。

以该临时变量的第二次赋值动作为界,修改此前对该临时变量的所有引用点,让它们引用新的临时变量。

在第二次赋值处,重新声明原先那个临时变量。编译测试。

逐次重复上述过程。每次都在声明处对临时变量改名,并修改下次赋值之前的引用点。


6.7.Remove Assignments to Parameters(移除对参数的赋值)(131)

代码对一个参数进行赋值。

以一个临时变量取代该参数的位置。

动机:不要对函数中传入的参数赋值

做法:建立一个临时变量,把待处理的参数值赋予它。

以“对参数的赋值”为界,将其后所有对此参数的引用点,全部替换为“对此临时变量的引用”。

修改赋值语句,使其改为对新建之临时变量赋值。编译,测试。


6.8.Replace Method with Method Object(以函数对象取代函数)(135)

你有一个大型函数,其中对局部变量的使用使你无法采用Extract Method(110)

将这个函数放进一个独立对象中,如此一来局部变量就成了对象内的字段。然后你可以在同一个对象中将这个大型函数分解为多个小型函数。

动机:Replace Temp with Query(120)无法分解函数

做法:建立一个新类,根据待处理函数的用途,为这个类命名。在新类中建立一个final字段,用以保存原先大型函数所在的对象。我们将这个字段称为“源对象”。同事,针对原函数的每个临时变量和每个参数,在新类中建立一个对应的字段保存之。

在新类中建立一个构造函数,接收源对象及原函数的所有参数作为参数。

在新类中建立一个compute()函数。

将原函数的代码赋值到compute()函数中。如果需要调用源对象的任何函数,请通过源对象字段调用。编译。

将旧函数的函数本体替换为这样一条语句:“创建上述新类的一个新对象,而后调用其中的compute()函数”。


6.9.Substitute Algorithm(替换算法)(139)

你想要把某个算法替换为另一个更清晰的算法。

将函数本体替换为另一个算法。

动机:算法太渣了

做法:换算法。


第七章 在对象之间搬移特性

7.1.Move Method(搬移函数)(142)

你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。

在该函数最常用引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除。

动机:如果一个类有太多行为,或如果一个类与另一个类有太多合作而形成高度耦合。

做法:检查源类中被源函数所使用的一切特性(包括字段和函数),考虑它们是否也该被搬移。

检查源类的子类和超类,是否有该函数的其他声明(多态性)。

在目标类中声明这个函数。

将源函数的代码赋值到目标函数中。调整后者,使其能在新家中正常运行。编译目标类。

决定如何从源函数正确引用目标对象。

修改源函数,使之成为一个纯委托函数。编译测试。

决定是否删除源函数,或将它当做一个委托函数保留下来。

如果要移除源函数,请将源类中对源函数的所有调用,替换为对目标函数的调用。编译测试。


7.2.Move Field(搬移字段)(146)

你的程序中,某个字段被其所驻类之外的另一个类更多地用到。

在目标类新建一个字段,修改字段的所用用户,令它们改用新字段。

动机:对于一个字段,在其所驻类之外的另一个类中有更多函数使用了它

做法:如果字段的访问级是public,使用Encapsulate Field(206)将它封装起来。编译测试。

在目标类中建立与源字段相同的字段,并同时建立相对应的设值/取值函数。编译目标类。

决定如何在源对象中引用目标对象。删除源字段。

将所有对源字段的引用替换为对某个目标函数的调用。编译测试。


7.3.Extract Class(提炼类)

某个类做了应该由两个类做的事。

建立一个新类,将相关的字段和函数从旧类搬移到新类。

动机:如果发现子类化只影响类的部分特性,或发现某些特性需要以一种方式来子类化,某些特性则需要以另外一个方式子类化。

做法:决定如何分解类所负的责任。

建立一个新类,用以表现从旧类中分离出来的责任。

建立“从旧类访问新类”的连接关系。

对于你想搬移的每个字段,运用Move Field(146)搬移之。每次搬移后,编译测试。

使用Move Method(142)将必要函数搬移到新类。先搬移较低层函数(也就是“被其他函数调用”多于“调用其他函数”者),再搬移较高层函数。每次搬移后,编译测试。

检查,精简每个类的接口。

决定是否公开新类。如果你的确需要公开它,就要决定让它成为引用对象还是不可变的值对象。


7.4.Inline Class(将类内联化)(与上一个相反)(154)

某个类没有做太多事情。

将这个类的所有特性搬移到另一个类,然后移除原类。

动机:一个类不再承担足够责任、不再有独立存在的理由

做法:在目标类身上声明源类的public协议,并将其中所有函数委托至源类。

修改所有源类引用点,改而引用目标类。编译测试。

运用Move Method(142)和Move Field(146),将源类的特性全部搬移至目标类。

。。。(为源类举行一个简单的“丧礼”)


7.5.Hide Delegate(隐藏“委托关系”)(157)

客户通过一个委托类来调用另一个对象。

在服务器上建立客户所需的所有函数,用以隐藏委托关系。

动机:某个客户先通过服务对象的字段得到另一个对象,然后调用后者的函数,那么客户就必须知晓这一层关系。(说白的了就是想对调用者隐藏实例变量下的函数)

做法:对于每一个委托中的函数,在服务对对象端建立一个简单的委托函数。

调整客户,令它只调用服务对象提供的函数。每次调整后,编译测试。

如果将来不再有任何客户需要取用Delegate,便可移除服务对象中的相关访问函数。便已测试。


7.6.Remove Middle Man(移除中间人)(160)(与上一个相反)

某个类做了过多的简单委托动作。

让客户直接调用受托类。

动机:每当客户要使用受托类的新特性时,你就必须在服务端添加一个简单委托函数。随着受托类的特性(功能)越来越多,服务类完全变成了一个“中间人”

做法:建立一个函数,用以获得受托对象。

对于每个委托函数,在服务类中删除该函数,并让需要调用该函数的客户转为调用受托对象。

处理每个委托函数后,编译测试。


7.7.Introduce Foreign Method(引入外加函数)

你需要为提供服务的类添加一个函数,但你无法修改这个类。

在客户类中建立一个函数,并以第一个参数形式传入一个服务类实例。

动机:无法提供新服务。(类似便利构造器)

做法:在客户类中建立一个函数,用来提供你需要的功能。

以服务类实例作为该函数的第一个参数。

将该函数注释为“外加函数(foreign method),应在服务类实现。”


7.8.Introduce Local Extension(引入本地扩展)(164)

你需要为服务类提供一些额外函数,但你无法修改这个类。

建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。

动机:上一个搞不定的时候。。。(好吧我没看懂)

做法:建立一个扩展类,将它作为原始类的子类或包装类。

在扩展类中加入转型构造函数。在扩展类中中加入新特性。

根据需要,将原对象替换为扩展对象。将针对原始定义的所有外加函数搬移到扩展类中。



0 0
原创粉丝点击