我正在尝试设计将在我的应用程序内部使用的界面。以Google为例,我努力减少公共API的混乱。但是,有一些便利方法是根据最小方法定义的。当我在方便和整洁之间寻求平衡时,我应该考虑哪些因素?
Google示例:在HashBiMap
(doc)中:
为什么BiMap没有 getKeyForValue()方法?
我们确实考虑过了(Doug Lea甚至 半开玩笑地建议命名它 TEG()!)。但你真的不需要它; 只需调用inverse()。get()。
Set
界面上的示例:add()
和remove()
是最小方法,而addAll()
和removeAll()
是为了方便起见。 addAll()
可以add()
来实现,因此它并没有真正为客户提供使用Set
的新功能。但它确实清理了客户端代码。
我考虑过制作一个包含更多便利方法的Utility
课程。但是后来我离开了OOP,我必须在每次调用中包含被操作的对象作为参数。虽然我想这是Java的Collections
类的例子。
答案 0 :(得分:4)
如果类有可能(即使它今天没有)以比客户端更有效的方式实现该API,我肯定会提供额外的API。 (例如,Set.removeAll()
。)一般来说,只要清理客户端代码,我就会提供额外的API。
您是否可以提供一个Google API的示例,它不提供看似有用的便捷方法,而是让客户端进行多次通话?
答案 1 :(得分:4)
提供更多方法会使重写虚拟方法变得更加困难/危险。
考虑例如add()和addAll()。 addAll()调用add()吗?它可以(它可能是一个简单的包装器,依次调用每个元素的add()),但它不必。因此,如果您再进行子类化,并添加一些新的不变量(例如,您可以将add()添加到单独的容器中以存储插入顺序,或者其他任何情况,那么在不同应用程序中有用的容器有很多变体) ,你现在必须知道addAll()是否调用add()。如果确实如此,那么您的子类可以保持正确的行为。但它没有!
当然,您可以通过适当的文档解决所有这些问题。但它使危险的事情变得更容易。
一般来说,更好的方法是使类接口最小化,正交化和完整化,然后添加make这些便利实用程序方法非成员非朋友。通过这样做,明确表明他们只能调用公共接口,从而避免了整个问题。
偶尔会出现一种情况,即将实用程序设为方法(而不是非成员非朋友)可以提供某些实现优势。一个例子是排序;通常排序(数组,deques,向量等)应该是非成员非朋友,但对于链表,使sort()成为方法是特别有利的。具体来说,一种方法可以操纵节点链接,从而使用就地合并排序 - 对于任何合理的链接列表界面来说都是困难的或不可能的。在这些特殊情况下,我建议使实用程序方法不可覆盖,并明确指出它们调用哪些方法(以及哪些方法有意义,以哪种顺序)。这最大化了子类不会破坏事物的机会。
答案 2 :(得分:1)
有一些可能的方法。我在其他地方看到的一个是使用最小的核心API,然后是“扩展”或“实用程序”API,这使得核心更方便,但不保证也不会得到支持。
通常,一旦您的开发人员社区变得足够大,人们就会为您的API编写自己的扩展,帮助程序和实用程序。
答案 3 :(得分:1)
一种解决方案是提供实现便捷方法的接口和抽象实现。例如,比较
interface List ...
和
class AbstractList implements List ...
java.util
包中的。所以客户端可以从抽象类中继承子类,然后实现抽象方法。
然而就个人而言,将便捷方法放在实用程序类中我并不感到羞耻。你不能用破碎的语言编写纯OO。 Java遗漏的内容是traits或扩展方法。据我所知,正在讨论Java 7的扩展方法。
答案 4 :(得分:1)
我会捎带John F.的回答:
我希望所有我认为有用的方便方法,而不是其他所有方法。 Firefox,可以使用插件来处理这类事情。浏览器支持基本的浏览器应该;但是,根据我个人的喜好,我可以用插件来加强它。我在同一个角度看到便利方法。
请允许我添加我喜欢的任何模块,以便我可以使用我想要的方便方法。
这个方面有很多方面:
答案 5 :(得分:0)
只要实用方法非常有用,而不仅仅是你想象中的虚构(“如果狼人征服美国,它可能会有用”),我会把它们添加到原来的课程中。
按照您的示例,addAll()和removeAll()是合理的实用工具方法,应该添加到Set中。但是addEven()和removeEven()是不合理的,即使它们在某些特定情况下可能有用。
现在,如何检测哪些方法合理?只有两种方式:经验和经验。
您必须在真实场景中尝试您的课程,以了解哪些实用方法在一般情况下非常有用。
答案 6 :(得分:0)
在没有查看源代码的情况下,我猜想ArrayList.addAll首先确保容量足够大,以便在操作发生时不调整数组大小。这对于实用程序类是不可能的,因为它是内部实现细节。
所以答案取决于你是否要求类的内部能够执行实用方法。如果没有,则有一个强有力的论据可以将它移出课堂,否则它应该是课堂的一部分。
答案 7 :(得分:0)
另一个参考点:Java的List
同时包含add()
和addAll()
。
我认为重要的是在你自己的脑海中清楚这是基本的,也是方便的。也称为 atomic (不能进一步细分), compound (可以通过组合原子方法形成)。
了解基本方法对于证明代码的内容非常有用;并确保您的用户真的可以对您的代码执行任何操作,因此完成(例如,确保在便捷方法中没有仅可用的功能)。
换句话说:图书馆的目的是有用的。便利方法使其更容易可用 - 但如果库首先没用,它们就无济于事。基本方法有助于确保您的代码完整(数学完美的一个方面) - 但如果您的库首先没用,它们也无济于事。
换句话说:100%专注于使其有用,并让可用性和完整性从那里流出。
答案 8 :(得分:0)
我提供更简化的API的首选方法是为用户提供更多功能,如果他们想要的话,那就是让API实现一个提供核心命令子集的接口。通过这种方式,用户可以通过界面访问它(如果他们想要简单的话),如果他们想要访问addAllUsersExceptOnesNamedJeff(),可以通过实际的类访问。