假设我们有一个API。
interface Graph {
boolean checkIfPathExists(source, destination);
List<Integer> getPath(source, destination);
}
以下哪一项是最佳/推荐的实施方式。
选项1:执行检查以验证路径是否存在。如果之前未调用checkPath,则抛出异常。
Graph g = new GraphImpl(graph);
if (g.checkIfPathExists(s, d)) {
List path = g.getPath(s, d);
}
选项2:不强制执行功能序列。如果调用了getPath,那么让getPath()在内部调用checkIfPathExists()并在checkIfPathExists()返回false时返回空集合;
Graph g = new GraphImpl(graph);
List path = g.getPath(s, d);
在我看来,Option2对我来说看起来不错,但只有问题,是对'状态检查方法'如'checkIfPathExists()'的冗余/不必要的调用。理想情况下,这不是getPath()应该做的事情。它应该只是返回路径。
哪个更好的实现option1或option2?
如果option2是一个更好的选择,那么将“checkIfPathExists”添加为公共界面是不是更好的设计选择呢?为了提出一个问题,如果没有强制执行,则更为通用的状态检查方法看起来像'hasNext()'。即使遍历列表,我们也可以获得列表的大小并使用for循环直到i <= size,并且根本不使用hasNext()。 简而言之 - 如果不执行状态检查功能,它们的用途是什么?
强制执行状态检查方法的任何“现实”示例?意思是,不是先调用它们会导致异常吗?
答案 0 :(得分:2)
我不知道这是什么类型的API,但我不认为两个顶点之间不存在路径是一种特殊情况,所以抛出和异常似乎并不好想法。
如果您正在争取minimal interface,那么您可以从您的API中删除boolean checkIfPathExists(source, destination);
并使用Guava的Optional类作为返回值getPath(source, destination);
来指示路径可能不存在于两个顶点之间。请记住,您必须区分不存在的路径和零长度路径。
另一方面,如果您想提供human interface,那么您可以保留boolean checkIfPathExists(source, destination);
作为方便的方法来检查顶点之间是否存在路径而不强制客户端实际检索它(这甚至可能为性能优化提供一些空间。请注意,这只是一个便利性方法,在getPath
之前调用它并不是强制性的,我认为这甚至不是一个好的设计,因为它会导致sequential coupling
最后,如果您无法确定哪个更好,那么坚持使用最小的API,以后更容易扩展。记住约书亚布洛赫的黄金法则:
如有疑问,请将其删除
答案 1 :(得分:0)
是否拥有checkIfExists
或hasNext
函数只是为了“保护”对另一个函数的调用,实际上取决于受保护函数的成本和后果。
如果getPath
非常昂贵并且checkIfPathExists
非常快,那么拥有这两个功能是有意义的。然而,在checkIfPathExists
之前执行对getPath
的相同参数的调用是不寻常的,并且似乎没有必要,因为如果你可以将该机制放入其中,那么你也可以缓存其结果,因此不需要实际强制执行它在getPath
中,只需使用缓存(如果可用)或执行调用。
对于Iterator
hasNext
的情况,部分原因是对next
的调用会产生后果并且成本未知(可能会从数组中迭代任何内容)单个链接列表使用网络连接的东西)。调用next
后,您也无法返回,因此在处理当前元素的逻辑根据是否存在下一个元素的情况下,Iterator需要支持它。
如果你特别担心性能,那么你可以提供两种方法,一种假设路径确实存在(checkIfPathExists
已被调用并返回true),因此不需要执行存在检查,没有这样的假设,在搜索之前会调用checkIfPathExists
。在javadoc中适当地记录方法,用户通常会正确使用它们。
直接回答您的问题,选项2 通常是首选,因为它更受期待,并且不需要额外的代码复杂性来验证调用顺序。规定首先调用checkIfPathExists
的优点(如果存在)的文档足以解决对此选项的任何担忧。