在对象技术中,最重要的概念莫过于"接口"(interface)。容易被理解和被使用的接口,是开发良好面向对象软件的关键。本章将介绍几个使接口变得更简洁易用的重构手法。
  最简单也最重要的一件事就是修改函数名称。名称是程序写作者与阅读者交流的关键工具。只要你能理解一段程序的功能,就应该大胆地使用RenameMethod(273)将你所知道的东西传达给其他人。另外,你也可以(并且应该)在适当时机修改变量名称和类名称。不过,总体来说,修改名称只是相对比较简单的文本替换功夫,所以我没有为它们提供单独的重构项目。

  函数参数在接口之中扮演十分重要的角色。Add Parameter(275)和Remove Parameter(277)都是很常见的重构手法。刚接触面向对象技术的程序员往往使用很长的参数列,这在其他开发环境中是很典型的方式。但是,使用对象技术,你可以保持参数列的简短,以下有一些相关的重构可以帮助你缩短参数列。如果来自同一对象的多个值被当作参数传递,你可以运用Preserve Whole Object(288)将它们替换为单一对象,从而缩短参数列。如果此前并不存在这样一个对象,你可以运用Introduce Parameter Object(295)将它创建出来。如果函数参数来自该函数可获取的一个对象,则可以使用Replace ParameterwithMethod(292)避免传递参数。如果某些参数被用来在条件表达式中做选择依据,可以实施Replace Parameter with Explicit AMethod(285)。另外,还可以使用Parameterize Method(283)为数个相似函数添加参数,将它们合并到一起。

  关于缩减参数列的重构手法,DougLea对我提出了一个警告∶并发编程往往需要使用较长的参数列,因为这样你可以保证传递给函数的参数都是不可被修改的,例如内置型对象和值对象一定是不可变的。通常,你可以使用不可变对象取代这样的长参数列,但另一方面你也必须对此类重构保持谨慎。

  多年来,我一直坚守一个很有价值的习惯∶明确地将"修改对象状态"的函数(修改函数)和"查询对象状态"的函数(查询函数)分开设计。不知道多少次,我因为将这两种函数混在一起而麻烦缠身;不知道多少次,我看到别人也因为同样的原因而遇到同样的麻烦。因此,如果我看到这两种函数混在一起,就会使用Separate Query from Modifier(279)将它们分开。

  良好的接口只向用户展现必须展现的东西。如果一个接口暴露了过多细节,你可以将不必要暴露的东西隐藏起来,从而改进接口的质量。毫无疑问,所有数据都应该隐藏起来(希望你不需要我来告诉你这一点),同时,所有可以隐藏的函数都应该被隐藏起来。进行重构时,你往往需要暂时暴露某些东西,最后再以Hide.Method (303)和Remove Setting Method(300)将它们隐藏起来。

  构造函数是Java和C++中特别麻烦的一个东西,因为它强迫你必须知道要创建的对象属于哪一个类,而你往往并不需要知道这一点。你可以使用Replace Constructor with Factory Method(304)避免了解这不必要的信息。

  转型是Java程序员心中另一处永远的痛。你应该尽量使用Encapsulate Downcast (308)将向下转型封装隐藏起来,避免让用户做那种动作。

  和许多现代编程语言一样,Java也有异常处理机制,这使得错误处理相对容易一些。不习惯使用异常的程序员,往往会以错误代码表示程序遇到的麻烦。你可以使用Replace Error Code with Exception(310)来运用新的异常特性。但有时候异常也并不是最合适的选择,你应该实施Replace Exception with Test(315)先测试一番。

10.1 Rename Method(函数改名)

函数的名称未能揭示函数的用途
修改函数名称

做法

  1. 检查函数签名是否被超类或子类实现过.如果是,则需要针对没分实现分别进行下列步骤.
  2. 声明一个新函数,将它命名为你想要的的新名称.将就函数的代码复制到新函数中,并进行适当调整.
  3. 编译
  4. 修改旧函数,令它将调用转发给新函数.
  5. 编译,测试
  6. 找出旧函数的所有被引用点,修改他们,令他们改而引用新函数.每次修改后,编译并测试.
  7. 删除旧函数
  8. 编译,测试

10.2 Add Parameter (添加参数)

某个函数需要从调用端得到更多信息
为此函数添加一个对象参数,让该对象带进函数所需信息.

10.3 Remove Parameter(移除参数)

函数本体不再需要某个参数
将该参数去除

10.4 Separate Query From Modifier(将查询函数和修改函数分离)

某个函数既返回对象状态值,又修改对象状态.
建立两个不同的函数,其中一个负责查询,另一个负责修改.

10.5 Parameterize Method(令函数携带参数)

若干函数做了类似的工作,但在函数本体却包含了不同价值
建立单一函数,以参数表达那些不同的值

10.6 Replace Parameter with Methods(以明确函数取代参数)

你有一个函数,其中完全取决于参数值而餐区不同行为.
针对该参数的每一个可能值,建立一个独立函数.

10.7 Preserve Whole Object(保持对象完整)

你从某个对象中取出若干值,将他们作为某一次函数调用时的参数
改为传递整个对象

10.8 Replace Parameter with Methods(以函数取代参数)

对象调用某个函数,并将所得结果作为参数,传递给另一个函数.而接收该参数的函数本身也能够调用前一个函数.
让参数接收者取出该项函数,并直接调用前一个函数

10.9 Introduce Parameter Object (引入参数对象)

某些参数总是很自然地同时出现
以一个对象取代这些参数

10.10 Remove Setting Method (移除设值函数)

类中的某个字段应该在对象创建时被设值,然后就不再改变.
去掉该字段的所有设值函数

10.11 Hide Method (隐藏函数)

有一个函数,从来没有被其他任何类用到
将这个函数修改为private.

10.12 Replace Constructor with Factory Method(以工程函数取代构造函数)

你虚妄在创建对象时不仅仅是做简单的构建动作
将构造函数替换为工厂函数.

10.13 Encapsulate Downcast (封装向下转型)

某个函数返回的对象,需要由函数调用者执行向下转型
将向下转型动作移动到函数中.

10.14 Replace Error Code with Exception(以异常取代错误码)

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

考虑非受控异常

10.15 Replace Exception with Test(以测试取代异常)

面对一个调用这个可以预先检查的条件,你抛出了一个异常.
修改调用者.使它在调用函数之前先做检查.