重构第10章

第十章、简化函数调用

       名称是程序写作者与阅读者交流的关键工具,所以使用函数改名能帮助其他阅读者更好的理解程序。函数参数在接口之中扮演十分重要的角色,添加参数和移除参数都是很常用的重构手法,如果来自同一对象的多个值被当做参数传递,可以运用保持对象完整将它们替换为单一对象,从而缩短参数列。如果此前并不存在这样一个对象可以运用引入参数对象将它创建出来。如果函数参数来自该函数可获取的一个对象,则可以使用以函数取代参数避免传递参数。如果某些条件被用在条件表达式中做选择依据,可以实施以明确函数取代参数。另外,还可以使用令函数携带参数为数个相似函数添加参数,将它们合并到一起。

10.1、函数改名

什么时候用?

函数名称并没有揭示函数的用途

怎么用?

修改函数名称

具体步骤:

1、检查函数签名是否被超类或子类实现过,如果是则需要针对每份实现分别进行下列步骤

2、声明一个新函数,将它命名为你想要的新名称。将旧函数的代码复制到新函数中,并进行适当的调整然后编译。

3、修改旧函数,令它将调用转发给新函数,编译并测试

4、找出就函数的所有被引用点,修改它们,令它们改而引用新函数,每次修改后,编译并测试。

5、删除旧函数(如果旧函数是该类public接口的一部分,那么可能无法安全的删除它,这种情况下将它保留在原处并标记为deprecated),编译并测试

10.2、添加参数

什么时候用?

某个函数需要从调用端得到更多信息

怎么用?

为此函数添加一个对象参数,让该对象带进函数所需信息

具体步骤:

1、检查函数签名是否被超类或子类实现过,如果是则需要针对每份实现分别进行下列步骤

2、声明一个新函数,名称与原函数相同,只是加上新添参数。将旧代码复制到新函数中(如果需要),编译

3、修改旧函数,令它调用新函数,编译测试

4、找出所有旧函数的所有被引用点,将它们全部修改为对新对象的引用。每次修改后进行编译测试。

5、删除旧函数(如果旧函数是该类public接口的一部分,那么可能无法安全的删除它,这种情况下将它保留在原处并标记为deprecated),编译并测试

10.3、移除参数

什么时候用?

函数本体不需要某个参数

怎么用?

将该参数去除

具体步骤:

移除参数、函数改名、添加参数的步骤十分相似

1、检查函数签名是否被超类或子类实现过,如果是则需要针对每份实现分别进行下列步骤

2、声明一个新函数,名称与原函数相同,只是移除不必要的参数。将旧代码复制到新函数中(如果需要),编译

3、修改旧函数,令它调用新函数,编译测试

4、找出所有旧函数的所有被引用点,将它们全部修改为对新对象的引用。每次修改后进行编译测试。

5、删除旧函数(如果旧函数是该类public接口的一部分,那么可能无法安全的删除它,这种情况下将它保留在原处并标记为deprecated),编译并测试

由于添加和去除参数都很简单,所以可以一次性地添加或去除多个参数。

10.4、将查询函数和修改函数分离

什么时候用?

某个函数即返回对象状态值又修改对象状态

怎么用?

建立两个不同的函数,其中一个负责查询,另一个负责修改

具体步骤:

1、 新建一个查询函数,令它返回的值与原函数相同(观察原函数,看它返回什么东西。如果是一个临时变量,找出临时变量的位置)

2、 修改原函数,令它调用查询函数,并返回获得的结果(如;return newQuery()),编译并测试。

3、将调用原函数的代码改为调用查询函数。然后,在调用查询函数的那一行之前,加上对原函数的调用。每次修改后编译并测试。

4、将原函数的返回值改为void,并删掉其中所有的return语句。

10.5、令函数携带参数

什么时候用?

若干函数做了类似的工作,但在函数本体中却包含了不同的值

怎么用?

建立单一函数,以参数表达那些不同的值。

具体步骤:

1、新建一个带有参数的函数,使它可以替换先前所有的重复性函数,编译。

2、将调用旧函数的代码改为调用新函数,然后编译并测试

3、对所有旧函数重复上述步骤,每次替换后,修改并测试

 有时候这种方法无法处理整个函数,但是可以处理函数中的一部分代码,这种情况下,应该首先将这部分代码提炼到一个独立函数中,然后再对那个提炼所得的函数使用令函数携带参数的重构手法。

10.6、以明确函数取代参数

什么时候用? (该重构手法和上面令函数携带参数手法刚好相反)

有一个函数,其中完全取决于参数值而采取不同行为

怎么用?

针对该参数的每一个可能值,建立一个独立函数

具体步骤:

1、针对参数的每一种可能值,新建一个明确函数

2、修改条件表达式的每个分支,使其调用合适的新函数

3、修改每个分支后,编译并测试

4、修改原函数的每一个被调用点,改而调用上述的某个合适的新函数,编译并测试

5、所有调用端都修改完毕后,删除原函数

10.7、保持对象完整

什么时候用?

从某个对象中取出若干值,将它们作为某一次函数调用时的参数

怎么用?

改而传递整个对象

具体步骤:

1、对你的目标函数新添一个参数项,用以代表原数据所在的完整对象,编译并测试

2、判断哪些参数可以被包含在新添的完整对象中

3、选择上述参数之一,将被调用函数中原来引用该参数的地方改为调用新添参数对象的相应取值函数。

4、删除该项参数,编译并测试

5、针对所有可从完整对象中获得的参数,重复上述过程

6、删除调用端中那些带有被删除参数的代码(如果调用端还在其他地方使用了这些参数,就不要删除它们),编译并测试

10.8、以函数取代参数

什么时候用?

对象调用某个函数,并将所得结果作为参数,传递给另一个函数,而接受该参数的函数本身也能够调用前一个函数。

怎么用?

让参数接受者去除该项参数,并直接调用前一个函数

具体步骤:

1、如果有必要,将参数的计算过程提炼到一个独立函数中。

2、将函数本体内引用该参数的地方改为调用新建的函数。

3、每次替换后,修改并测试

4、全部替换完成后,使用移除参数将该参数去掉。

10.9、引入参数对象

什么时候用?

某些参数总是很自然地同时出现

怎么用?

以一个对象取代这些参数

具体步骤:

1、新建一个类,用以表现想替换的一组参数,将这个类设为不可变然后编译

2、针对使用该组参数的所有函数,实施添加参数,传入上述新建类的实例对象,并将此参数值设为null(如果所修改的函数被其他很多函数调用,那么可以保留修改前的旧函数,并令它调用修改后的新函数。可以先对旧函数进行重构,然后逐一修改调用端使其调用新函数,最后再将旧函数删除。)

3、对于数据泥团中的每一项(在此均为参数),从函数签名中移除之,并修改调用端和函数本体,令它们都改而通过新的参数对象取得该值。

4、每去除一个参数,编译并测试

5、将原先的参数全部去除之后,观察有无适当函数可以运用搬移函数搬移到参数对象之中。(被搬移的可能是整个函数,也可能是函数中的一个段落,如果是后者,应先使用提炼函数将该段落提炼为一个独立函数,再搬移这一新建函数)


10.10、移除设置函数

什么时候用?

类中的某个字段应该在对象创建时被设值,然后就不再改变

怎么用?

去掉该字段的所有设值函数

具体步骤:

1、检查设值函数被使用的情况,看它是否只被构造函数所调用,或者被构造函数所调用的另一个函数调用。

2、修改构造函数,使其直接访问设值函数所针对的那个变量(如果某个子类通过设值函数给超类的某个private字段设了值,那么就不能这样修改,这种情况下应该试着在超类中提供一个protected函数(最好是构造函数)来给这些字段设值,不论怎么做都不要给超类中的函数起一个与设值函数混淆的名字),编译并测试

3、移除这个设值函数,将它所针对的字段设为final,编译并测试

 

10.11、隐藏函数

什么时候用?

有一个函数从来没有被其他任何类使用(当面对一个过于丰富、提供了过多行为的接口时,就值得将非必要的取值函数和设值函数隐藏起来)

怎么用?

将这个函数修改为private

具体步骤:

1、检查有没有可能降低某个函数的可见度

2、尽可能降低所有函数的可见度

3、每完成一组函数的隐藏后,编译并测试(如果有不适当的隐藏,编译器会检验出来)

 

10.12、以工厂函数取代构造函数

什么时候用?

希望在创建对象时不仅仅做简单的构建动作

怎么用?

将构造函数替换为工厂函数

具体步骤:

1、新建一个工厂函数,让它调用现有的构造函数

2、将调用构造函数的代码改为调用工厂函数

3、每次替换后,编译并测试

4、将构造函数声明为private并进行编译。

10.13、封装向下转型

什么时候用?

某个函数返回的对象,需要由函数调用者执行向下转型(downcast)

怎么用?

将向下转型动作移到函数中

具体步骤:

1、找出必须对函数调用结果进行向下转型的地方(这种情况通常出现在返回一个集合或迭代器的函数中)

2、将向下转型动作搬移到该函数中(针对返回集合的函数,使用封装集合)

 

10.14、以异常取代错误码

什么时候用?

某个函数返回一个特定的代码,用以表示某种错误情况。

怎么用?

改用异常

具体步骤:

1、决定应该抛出受控异常还是非受控异常(如果调用者有责任在调用前检查必要状态,就抛出非受控异常)

2、找到该函数的所有调用者,对它们进行相应的调整,让它们使用异常(如果函数抛出非受控异常,那么就调整调用者,使其在调用函数前做适当检查,每次修改后编译并测试。如果函数抛出受控异常,那么就调整调用者,使其在try区段中调用该函数)

3、修改函数签名,令它反映出新用法(如果函数有许多调用者,上述修改过程跨度太大,可以将它分成下列数个步骤)

4、决定应该抛出受控异常还是非受控异常

5、新建一个函数,使用异常来表示错误情况,将旧函数的代码复制到新函数中,并做适当的调整。

6、修改旧函数的函数本体,让它调用上述新建函数。编译并测试

7、逐一修改旧函数的调用者,令其调用新函数。每次修改完后,编译并测试

8、移除旧函数

 

10.15、以测试取代异常

什么时候用?

面对一个调用者可以预先检查的条件,抛出了一个异常

怎么用?

修改调用者,使它在调用函数之前先做检查

具体步骤:

1、在函数调用点之前,放置一个测试语句,将函数内catch区段的代码复制到测试语句适当的if分支中。

2、在catch区段起始处加入一个断言,确保catch区段绝对不会被执行,编译测试

3、移除所有catch区段,然后将try区段内的代码复制到try之外,然后移除try区段,编译并测试。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值