一、Rename Method(函数改名)
函数的名称未能揭示函数的用途
修改函数名称
动机
函数的名称应该准确表达它的用途。给函数命名有一个好的方法:首先考虑应该给这个函数写上一句怎样的注释,然后想办法将注释变成函数名称。
做法
1、检查函数签名是否被超类或子类实现过。如果是,则需要针对每份实现分别进行下列步骤。
2、声明一个新函数,将它命名为你想要的名称。将旧函数代码复制到新函数中,并进行适当的调整。
3、编译。
4、修改旧函数,令它将调用转发给新函数。
5、编译,测试
6、找出旧函数的所有被引用点,修改它们,令它们改而引用新函数。每次修改后,编译并测试。
7、删除旧函数。
? 如果旧函数是该类的public接口的一部分,你可能无法安全的删除它。这种情况下,将它保留在原处,并将它标记为deprecated(建议不使用)。
8、编译测试。
二、Add Parameters(添加参数)
某个函数需要从调用端得到更多信息。
为此函数添加一个对象参数,让该对象带进函数所需信息。
动机
Add Parameters是一个很常见的重构手法,我几乎可以肯定你已经用过它了。使用这项重构的动机很简单:你必须修改一个函数,而修改后的函数需要一些过去没有的信息,因此你需要给该函数添加一个参数。
实际上我要说明的是:不使用本项重构的时机。除了添加参数外,你常常还有其他选择。只要可能,其他选择都比添加参数要好,因为它们不会增加参数列的长度。
做法
1、检查函数签名是否被超类或子类实现过。如果是,则需要针对每份实现分别进行一下步骤。
2、声明一个新函数,名称与原函数相同,只是加上新添参数。将旧函数的代码复制到新函数中。
3、编译
4、修改旧函数,令它调用新函数。
5、编译,测试
6、找出旧函数的所有被引用点,将它们全部修改为新函数的引用。每次修改后,编译并测试。
7、删除旧函数。
8、编译测试。
三、Remove Parameter(移除参数)
函数本体不再需要某个参数。
将该参数移除。
动机
参数代表着函数所需的信息,不同的参数值有不同的意义。函数调用者必须为每一个参数操心该传什么东西进去。如果你不去掉多余参数,就是让你的每一位用户多费一份心。是很不划算的,更何况“去除参数”是非常简单的一项重构。
做法
1、检查函数签名是否被超类或子类实现过。如果是,则需要针对每份实现分别进行一下步骤。
2、声明一个新函数,名称与原函数相同,只是去掉不必要的参数。将旧函数的代码复制到新函数中。
3、编译
4、修改旧函数,令它调用新函数。
5、编译,测试
6、找出旧函数的所有被引用点,将它们全部修改为新函数的引用。每次修改后,编译并测试。
7、删除旧函数。
8、编译测试。
四、Separate Query from Modifier(将查询函数和修改函数分离)
某个函数既返回对象状态值,又修改对象状态。
建立两个不同的函数,其中一个负责查询,另一个负责修改。
动机
如果某个函数只是向你提供一个值,没有任何看到的副作用,那么这是个很有价值的东西。你可以任意调用这个函数,也可以把调用动作搬移到函数的其他地方。简而言之,需要操心的事情少多了。
明确表现出“有副作用”与“无副作用”两种函数之间的差异,是个很好的想法。下面是一条规则:任何有返回值的函数,都不应该有看得到的副作用。有些程序员甚至将此作为一条必须遵守的规则。就像对待任何东西一样,我并不绝对遵守它,不过我总是尽量遵守,而它也回报我很好的效果。
你也许已经注意到了:我使用“看得到的副作用”这种说法。有一种常见的优化办法是:将查询结果缓存到某个字段中,这么一来后续的重复查询就可以大大加快速度。虽然这种做法改变了对象的状态,但这一修改是察觉不到的,因为不论如何查询,你总是获得相同结果。
做法
1、新建一个查询函数,令它返回的值与原函数相同。
? 观察原函数,看它返回什么东西。如果返回的是一个临时变量,找出临时变量的位置。
2、修改原函数,令它调用查询函数,并返回获得的结果。
? 原函数中的每一个return句都应该像这样:return newQuery(),而不应该返回其他东西。
? 如果调用者将返回值赋给了一个临时变量,你应该能够去除这个临时变量。
3、编译、测试。
4、将调用原函数的代码改为调用查询函数。然后,在调用查询函数的那一行之前,加上对原函数的调用。每次修改后,编译并测试。
5、将原函数的返回值改为void,并删掉其他所有的return语句。
并发问题
如果你在多线程系统中工作,肯定知道这样一个重要的惯用手法:在同一个动作中完成检查和赋值。这是否和Separate Query from Modifier互相矛盾呢?两者并不矛盾,但你需要做一些额外工作。将查询动作和修改动作分开来仍然是有价值的。但你需要保留第三个函数来同时做着两件事。这个“查询-修改”函数将调用各自独立的查询函数和修改函数,并被声明为synchronized。如果查询函数和修改函数未被声明为synchronized,那么你还应该将它们的可见范围限制在包级别或private级别。这样,你就可以拥有一个安全、同步的操作,它由两个较易理解的函数组成。这两个较低层函数也可以用于其他场合。
五、Parameterize Method(令函数携带参数)
若干函数做了类似的工作, 但在函数本体中却包含了不同信息。
建立一个单一函数,以参数表达那些不同。
动机
你可能会发现这样的两个函数:它们做着类似的工作,但因少数几个值致使行为略有不同。这种情况下,你可以将这些个字分离的函数同一起来,并通过参数来处理那些情况,用以简化问题。这样的修改可以去除重复的代码,并提高灵活性,因为你可以用这个参数处理更多的情况。
做法
1、新建一个带有参数的函数,使它可以替换先前所有的重复性函数。
2、编译
3、将调用旧函数的代码改为调用新函数。
4、编译、测试。
5、对所有旧函数重复上述步骤,每次替换后,修改并测试。
也许你会发现,你无法用这种办法处理整个函数,但可以处理函数中的一部分代码。这种情况下,你应该首先将这部分代码提炼到一个独立函数中,然后再对那个提炼所得的函数使用Parameterize Method。