有没有理由避免Java中的哨兵模式?

时间:2009-11-10 17:28:37

标签: java design-patterns

我听说过人们应该总是使用Iterator pattern来控制循环,而不是抛出异常(这是在Python iterators中完成的方式)或使用Sentinel pattern,从而返回一个特殊的标记值(通常为null)以指示迭代的结束。

最佳做法是否建议不要使用哨兵模式?如果是这样,为什么? (除了不使用Java 1.5中的foreach语法)。

编辑: 代码示例1 - Sentinel模式

Reader r = ...;
for( int val = r.read(); val != -1; val = r.read()) {
   doSomethingWith(val);
}

代码示例2 - 迭代器模式

for(Iterator<Thing> it = getAnIterator() ; it.hasNext(); ) {
  Thing t = it.next();
  doSomethingWith(t);
}

7 个答案:

答案 0 :(得分:15)

Sentinel模式的问题在于它明确地从作为有效元素的值集中排除了sentinel值。即,如果您有一个可以有效地包含null作为元素的对象列表,则使用null作为标记值是失败的。

答案 1 :(得分:9)

例外情况只能用于“例外”情况。结束或打破循环是正常的程序流程,而不是特殊情况。

此外,当使用Java(或任何语言)工作时,您希望使用社区中常见且众所周知的模式和约定,因为其他Java程序员可能需要维护您的代码。如果他们这样做,他们很可能会期望看到迭代器,而不是哨兵模式。

答案 2 :(得分:4)

我认为它们都可以,但Iterator在Java中更具惯用性(特别是如果你实际上有一个Iterable,你可以使用for-each循环)。

你所看到的一个地方就是用Java编写的Sentinel版本就是用I / O代码编写的。

答案 3 :(得分:2)

Sentinel模式在JDK中用于读取流,因此它并不是闻所未闻,但是制作循环是很尴尬的。所有解决方案都有负面因素,而迭代器则没有。您的解决方案需要重复调​​用(即代码重复)。 while循环强制将变量声明为循环范围之外。其他替代方案令人惊讶且难以理解。

所以最后迭代器是一种默认值,只能因某种原因而偏离。

答案 4 :(得分:2)

口头禅是“说出你做了什么,做你说的话。”

如果您将返回的值作为特殊值进行测试,则说明为什么要对此进行测试。在您的示例中:

for( int val = r.read(); val != -1; val = r.read()) {
   doSomethingWith(val);
}

这是否说“如果返回的值变为-1,我们可以跳过其余的”,或“如果返回的值始终为-1,则发生错误”或“如果返回的值始终为-1” ,达到了“?相比之下,hasNext完全是明确的。

顺便说一句,我实际上喜欢其他语言提供(或允许编写)的foreachmap构造比显式循环更好。

答案 5 :(得分:0)

我没有看到Sentinel模式与其自己的模式有多么不同。我唯一的想法是,从流数据中确定一个停止值会很有用。但是,interator使用“hasMore”方法要求来处理这个问题。

答案 6 :(得分:0)

“异常只应用于”例外“情况。结束或中断循环是正常的程序流程,而不是特殊情况。”

如果一个文件读取成功了一百万次并且只有最后一次它说“我找不到更多记录”,那你就不称之为“例外”吗?

我没有看到使用EOFExceptions控制流的问题(如果系统足够健全以提升它们,而不是返回那个非常愚蠢的-1整数,说“读取了-1个字节”)。你告诉我以下是不可读/不可维护的:

try {
    r = readNext(...);
    process(r);
} catch (EOFException e) {
    ...
}