3

我在这里研究了一段时间的 Java 流控制和异常处理,并且有这些普遍接受的规则:

  1. 不要将异常处理用于流控制
  2. 避免检查异常,除非客户端希望恢复(这是一种很好的说法,您正在强制客户端处理异常,以便他们不妨尝试从中恢复?)

除了一般规则外,我还尝试遵循:

  1. 如果 doSomething() 未能“做某事”,则应让调用者意识到这一点
  2. 方法应该关注做一件事

在某些情况下,这会造成不好的混合。我发现我在方法中捕获了已检查的异常,并在各处返回布尔值,并且不断地检查连续的调用,就像这样:

if(doA()){
  if(doB()){
    if(doC()){ 
      // only here do I know it's good
    }else{
      // I know C failed
    }
  }else{
    // I know B failed
  }
}else{
  // I know A failed
}

我在某些部分有 5-6 个嵌套的 if-else,这非常难看。更不用说管理一堆布尔变量来跟踪哪些有效,哪些无效。

有什么建议吗?这是一个可以接受“goto”的地方吗?

4

4 回答 4

5

您应该查看doA(),doB()doC(). 如果它们不太可能失败,则在它们失败时抛出异常。

try {
  doA();
  doB();
  doC();
} catch (FailureException e) {
  // handle failure
}

不合理失败的例子比比皆是,,,IOException等等IllegalParameterException

如果他们有合理的可能失败

if (!doA()) {
  // handle A's failure
  return;
}
if (!doB()) {
  // handle B's failure
  return;
}
if (!doC()) {
  // handle C's failure
  return;
}

Java 中不太强调合理失败的示例。一些示例包括read()在没有更多内容可阅读时返回 -1。如果您doA()的实际名称更接近,attemptA()那么返回一个boolean指示尝试成功的值可能是合适的。想想add(...)addAll(...)Collections界面中,true如果结果Collection被修改,它们就会返回。

在大多数语言中,传统goto语言并不是一个好的选择,因为在审查代码时,实际上不可能知道代码“来自何处”。在进入区块之前缺乏对状态的了解goto使得在进入区块之前无法保证一致的环境goto。顺便说一句,这就是为什么传统goto在 Java 中不可用,而只有有限的延续可用goto

要将嵌套不良的结构转换为嵌套较少的结构,请使用一些重构技术:

if(doA()){
  if (doB()) {
    if (doC()) { 
      // only here do I know it's good
    } else {
      // I know C failed
    }
  } else {
    // I know B failed
  }
} else {
  // I know A failed
}
return;

相当于

if (doA()) {
  if (doB()) {
    if (doC()) { 
      // only here do I know it's good
    } else {
      // I know C failed
    }
  } else {
    // I know B failed
  }
  return;
} else {
  // I know A failed
  return;
}

这相当于

if (!doA()) {
  // I know A failed
  return;
} else {
  if (doB()) {
    if (doC()) { 
      // only here do I know it's good
    } else {
      // I know C failed
    }
  } else {
    // I know B failed
  }
  return;
}

如果“我知道A失败”中的代码包含了return,那么你不必担心条件doA()为真的代码会落入下面的代码中;因此您可以提升较低的块,如下所示:

if (!doA()) {
  // I know A failed
  return;
}
if (doB()) {
  if (doC()) { 
    // only here do I know it's good
  } else {
    // I know C failed
  }
} else {
  // I know B failed
}
return;
于 2011-04-13T19:39:21.083 回答
1

是的。使用异常而不是返回码。如果愿意,您可以自己包装异常。

在异常情况下使用异常进行流控制是完全合理的。通常的建议是避免将它们用于普通的流量控制。

于 2011-04-13T19:36:50.740 回答
0

我认为您的规则“除非客户希望恢复,否则不要使用已检查的异常”是错误的。让调用者根本不恢复是很常见的,而是将异常传递给它的调用者,可能会将异常转换为其他东西。

于 2011-04-13T19:37:21.607 回答
0

我会使用状态模式。这样做可以使你的代码容易阅读,因为你只能看到你可以从你所处的当前状态进入哪些可能的状态。

于 2011-04-13T19:57:05.410 回答