9

我正在尝试设计将在内部用于我的应用程序的界面。效仿 Google 的例子,我努力减少公共 API 的混乱。但是,有一些便利方法是根据最小方法定义的。当我在方便和整洁之间寻求平衡时,我应该考虑哪些因素?

谷歌示例:在HashBiMap文档)中:

为什么 BiMap 没有 getKeyForValue() 方法?

我们确实考虑过(Doug Lea 甚至半开玩笑地建议将其命名为 teg()!)。但你并不真的需要它;只需调用 inverse().get()。

Google 收藏集常见问题解答

接口上的一个例子Setadd()andremove()是最小的方法,而addAll()andremoveAll()是为了方便起见。addAll()可以add()根据Set. 但它确实清理了客户端代码。

我考虑过创建一个Utility包含更多便利方法的类。但是后来我离开了 OOP,我必须在每次调用中包含正在操作的对象作为参数。虽然我猜这遵循 JavaCollections类的例子。

4

9 回答 9

4

提供更多方法会使覆盖虚拟方法更加困难/危险。

例如考虑 add() 和 addAll()。addAll() 是否调用 add()?它可以(它可以是一个简单的包装器,依次为每个元素调用 add() ),但它不是必须的。因此,如果您随后子类化并添加一些新的不变量(例如,您让 add() 将内容添加到单独的容器中以存储插入顺序,或者其他任何东西,容器上有许多变体在不同的应用程序中很有用) ,您现在必须知道 addAll() 是否调用 add()。如果确实如此,那么您的子类将保持正确的行为。但这不是必须的!

当然,您可以通过适当的文档解决所有这些问题。但它使危险的事情更容易做。

一般来说,更好的方法是使类接口最小、正交和完整,然后添加使这些便利实用程序方法non-member non-friends。通过这样做,很明显他们只能调用公共接口,从而避免了整个问题。

偶尔会出现这样一种情况,使实用程序成为方法(而不是非成员非朋友)提供了一些实现优势。这方面的一个例子是排序;通常排序(数组、双端队列、向量等)应该是非成员非友元,但对于链表来说,使 sort() 成为一种方法有一个特别的优势。具体来说,一种方法可以操纵节点链接,从而使用就地合并排序——这对于任何合理的链表接口来说都是困难或不可能的。在这些特殊情况下,我建议使实用程序方法不可覆盖,并明确指出它们调用的方法(以及,在哪里有意义,以什么顺序)。这最大限度地提高了子类不会破坏事物的机会。

于 2010-01-01T05:34:25.900 回答
4

只要类有机会(即使现在没有)以比客户端更有效的方式实现该 API,我肯定会提供额外的 API。(例如,Set.removeAll()。)一般来说,只要它清理客户端代码,我就会提供额外的 API。

您能否提供一个 Google API 的示例,它没有提供看似有用的便利方法来支持让客户端进行多次调用?

于 2010-01-01T04:57:29.460 回答
2

我会捎带 John F. 的回答:

我想要所有我觉得有用的便捷方法,而没有其他我不想要的方法。Firefox,通过插件来适应这种事情。浏览器支持基本浏览器应具备的功能;但是,根据我自己的个人喜好,我可以使用插件来加强它。我从同样的角度看待便利方法。

允许我添加我喜欢的任何模块,这样我就可以拥有我想要的便捷方法。

这个有很多方面:

http://martinfowler.com/bliki/HumaneInterface.html

于 2010-08-09T20:27:58.953 回答
1

一种解决方案是同时提供一个接口和一个实现便利方法的抽象实现。例如,比较

interface List ...

class AbstractList implements List ...

java.util包中。所以客户端可以从抽象类继承,只实现抽象方法。

然而,就我个人而言,将便利方法放在实用程序类中我不会感到羞耻。你不能用不成熟的语言编写纯 OO。Java 在这里缺少的是特征或扩展方法。据我所知,Java 7 的扩展方法正在讨论中。

于 2010-01-01T05:13:25.600 回答
1

有几种可能的方法。我在其他地方看到使用的一种方法是拥有一个最小的核心 API,然后是一个“扩展”或“实用程序”API,这使得核心更方便,但不能保证也或根本不支持。

通常,一旦您的开发人员社区变得足够大,人们就会为您的 API 编写自己的扩展、帮助程序和实用程序。

于 2010-01-01T04:59:56.860 回答
0

另一个参考点:JavaList同时具有add()addAll().

我认为重要的是在你自己的头脑中清楚哪些是基本的,哪些是方便的。也分别称为原子(不能再细分)和化合物(可以通过结合原子方法形成)。

了解基本方法对于证明代码非常有用;并确保您的用户真的可以对您的代码做任何事情,因此它是完整的(例如,确保没有在便利方法中可用的功能)。

换句话说:你的图书馆的目的是有用的。方便的方法使它更有- 但如果库一开始就没有用,它们就无济于事。基本方法有助于确保您的代码是完整的(数学完美的一个方面) - 但如果您的库一开始就没有用,它们也无济于事。

换句话说:100% 专注于让它变得有用,并让可用性和完整性从那里流动。

于 2010-01-01T05:20:40.337 回答
0

我提供更简化的 API 的首选方法是让用户获得更多功能(如果他们想要的话)是让 API 实现一个提供核心命令子集的接口。这样,用户如果想要简单可以通过接口访问它,如果想要访问 addAllUsersExceptOnesNamedJeff(),可以通过实际类访问。

于 2010-01-01T13:29:42.293 回答
0

只要实用程序方法真的有用,而不仅仅是您的想象(“如果狼人征服美国,它可能会有用”),我会将它们添加到原始类中。

按照您的示例,addAll() 和 removeAll() 是合理的实用方法,应该添加到 Set。但是 addEven() 和 removeEven() 是不合理的,即使它们在某些特定情况下可能有用。

现在,如何检测哪些方法是合理的?只有两种方式:体验和体验。

您必须在现实生活场景中尝试您的课程,以了解哪些实用方法在一般情况下真正有用。

于 2010-01-01T05:03:26.440 回答
0

如果不查看源代码,我会猜测 ArrayList.addAll 首先确保容量足够大,以便在操作发生时不会调整数组的大小。这对于实用程序类是不可能的,因为它是一个内部实现细节。

所以答案取决于你是否需要类的内部能够执行实用程序方法。如果不是,那么有一个强有力的论据将其移出类,否则它应该是类的一部分。

于 2010-01-01T05:09:59.820 回答