121 Star 1K Fork 302

lin-mt/effective-java-third-edition

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
第51项:谨慎设计方法签名.md 6.02 KB
一键复制 编辑 原始数据 按行查看 历史
lin-mt 提交于 2020-01-10 20:55 . 修订51、52项

谨慎设计方法签名

  本项是若干 API 设计技巧的总结,它们还不足以单独开设一个项。总的来说,这些设计技巧将有助于使你的 API 更易于学习和使用,并且比较不容易出错。

  谨慎地选择方法的名称。方法的名称应该始终遵循标准的命名习惯(第 68 项)。首要目标应该是选择易于理解的,并且与同一个包中的其他命名风格一致的名称。第二个目标应该是选择广泛认可的命名【方式】(如果存在的话)相一致的名称。避免使用长的方法名称。如果还有疑问,请参考 Java 库的 API。虽然它们存在许多不一致的地方,考虑到这些库的规模和范围,这是不可避免的,但它还是得到了相当程度的认可。

  不要过于追求提供遍历的方法。每个方法都应该尽其所能。方法太多会使类难以学习、使用、文档化、测试和维护。对于接口而言,这无疑是正确的,方法太多会使接口实现者和接口使用者的工作变得复杂起来。对于类和接口所支持的每个动作,都提供一个功能齐全的方法。只有当一项操作被经常用到的时候,才考虑为它提供快捷方式(shorthand)。如果不能确定,还是不要提供快捷方式为好

  避免过长的参数列表。目标是四个参数,或者更少。大多数程序猿都无法记住更长的参数列表。如果你编写的许多方法都超过了这个限制,你的 API 就不太便于使用,除非用户不停地参考它的文档。现代的 IDE 会有所帮助,但最好还是使用简短的参数列表。相同类型的长参数序列格外有害。用户不仅无法记住参数的顺序,而且,当他们不小心弄错了参数顺序时,他们的程序仍然可以编译和运行,只不过这些程序不会按照作者的意图进行工作。

  有三种方法可以缩短过长的参数列表。第一种是把方法分解成多个方法,每个方法只需要这些参数的一个子集。如果不小心,这样做会导致方法过多。但是通过提示它们的正交性(orthogonality),还可以*减少(reduce)*方法的数目。例如,考虑 java.util.List 接口。它并没有提供“在子列表(sublist)中查找元素的第一个索引和最后一个索引”的方法,这两个方法都需要三个参数。相反,它提供了 subList 方法,这个方法带有两个参数,并返回子列表的一个视图(view)。这个方法可以与 indexOf 或者 lastIndexOf 方法结合起来,获得期望的功能,而这两个方法都分别只有一个参数。而且,subList 方法也可以与其他任何“针对 List 实例进行操作”的方法结合起来,在子列表上执行任意的计算。这样得到的 API 就有很高的“功能-重量”(power-to-weight)比。

  缩短长参数列表的第二种方法是创建辅助类(helper class),用来保存参数的分组。这些辅助类一般为静态成员类(第 24 项)。如果一个频繁出现的参数序列可以被看作是代表了某个独特的实体,则建议使用这种方法。例如,假设你正在编写一个表示纸牌游戏的类,你会发现,经常要传递两个参数的序列来表示纸牌的点数和花色。如果添加辅助类来表示一张纸牌,并且把每个参数序列都换成这个辅助类的单个参数,那么这个纸牌游戏类的 API 以及它的内部表示都可能会得到改进。

  结合了前两种方法特征的第三种方法时,从对象构建到方法调用都采用 Builder 模式(第 2 项)。如果方法带有多个参数,尤其是当它们中有些是可选的时候,最好定义一个对象来表示所有参数,并允许客户端在这个对象上进行多次“setter”调用,每次调用都设置一个参数,或者设置一个较小的相关的集合。一旦设置了需要的参数,客户端就调用对象的“执行(execute)”方法,它对参数进行最终的有效性检查,并执行实际的计算。

  对于参数类型,要优先使用接口而不是类(第 64 项)。只要有适当的接口可用来定义参数,就优先使用这个接口,而不是使用实现该接口的类。例如,没有理由在编写方法时使用 HashMap 类来作为输入,相反,应当使用 Map 接口作为参数。这使你可以传入一个 HashMap、TreeMap、 ConcurrentHashMap、TreeMap 的子映射列表(submap),或者尚未编写的任何 Map 实现。如果使用的是类而不是接口,则限制了客户端只能输入特定的实现,如果碰巧输入的数据是以其他的形式存在,就会导致不必要的、可能非常昂贵的拷贝操作。

  对于 boolean 参数,要优先使用两个元素的枚举类型,除非方法名称中明确了布尔值的含义。枚举类型使你的代码更易于阅读和编写。而且它也使以后更易于添加更多的选项。例如,你可能会有一个 Thermometer 类型,它带有一个静态工厂方法,而这个静态工厂方法的签名需要传入这个枚举的值:

public enum TemperatureScale { FAHRENHEIT, CELSIUS }

  Thermometer.newInstance(TemperatureScale.CELSIUS)不仅比 Thermometer.newInstance(true)更有用,而且你还可以在未来的发行版本中将 KELVIN 添加到 TemperatureScale 中,无需非得给 Thermometer 添加新的静态工厂。你还可以将依赖于温度刻度单位的代码重构到枚举常量的方法中(第 34 项)。例如,每个刻度单位都可以有一个方法,它带有一个 double 值,并将它规格化为摄氏度。

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
其他
1
https://gitee.com/lin-mt/effective-java-third-edition.git
git@gitee.com:lin-mt/effective-java-third-edition.git
lin-mt
effective-java-third-edition
effective-java-third-edition
master

搜索帮助

A270a887 8829481 3d7a4017 8829481