If statement 如何编写方法的错误检查部分以使其可读并易于出错?

If statement 如何编写方法的错误检查部分以使其可读并易于出错?,if-statement,error-handling,coding-style,code-complete,If Statement,Error Handling,Coding Style,Code Complete,我与另一位程序员在如何编写包含大量错误检查的方法方面存在分歧: public void performAction() { if (test1) { if (test2) { if (test3) { // DO STUFF } else { return "error 3"; } } else { return "error 2"; } } else { return "er

我与另一位程序员在如何编写包含大量错误检查的方法方面存在分歧:

public void performAction() {
  if (test1) {
    if (test2) {
      if (test3) {
        // DO STUFF
      } else {
        return "error 3";
      }
    } else {
      return "error 2";
    }
  } else {
    return "error 1";
  }
}
-

对我来说,if语句的深度嵌套使得第一个示例很难阅读。
第二种方法尽管有三种回报,但更具可读性

我好奇地检查了CodeComplete是怎么说的,这让我对处理这个问题的方法不太确定:

嵌套底部的错误条件堆栈是编写良好的错误处理代码的标志

但是

在四个if语句中缩进例程的主体在美学上很难看,尤其是在最里面的if语句中有很多代码的情况下

并考虑使用保护条款,如第二个示例中所示

尽量减少每个例程中的返回次数。当你在底部阅读时,你没有意识到它可能会返回上面的某个地方,你就更难理解一个例程了


如何编写方法的错误检查部分以使其可读性和易出错?

没有什么比风格辩论()更能让程序员陷入战斗。因此,简短的回答是“您和您的团队决定的任何风格都最适合您的项目/语言。”

话虽如此,我想在CodeComplete关于多次返回的评论中加上我的2美分。您应该区分多次成功的回报和多次回报。如果我需要调查不是由于生成错误而导致的5次返回,则可能需要重写该函数。如果您在检测到错误后立即优雅地退出函数,那么维护程序员(即6个月后的您)在遵循函数的主要逻辑方面应该不会比嵌套所有这些错误检查更困难

因此,我个人的观点是,您的第二个代码示例是更好的选择。

这是我的观点

“尽量减少每次例行程序中的返回次数”这句老话似乎有点过时了。当您有8-10行更长的代码,执行大量操作时,它非常适用

新的思想流派强调单一责任和非常简短的方法,这似乎有点不必要。当您的整个方法不直接执行任何操作,而只是处理错误处理时,最好以干净的格式多次返回

无论哪种情况,只要有嵌套的ifs,可读性都会受到很大影响

我要做的一个优化是使用if-else-if结构,以清楚地指示逻辑流

示例代码:

public void Execute() 
{     
    if (test1)
    {
        return;
    }
    else if (test2)
    {
        return;
    }
    PerformAction();
}

private void PerformAction()
{
    //DO STUFF
}

如果您正在使用一种具有异常处理和自动资源管理功能的语言,那么您的同事可能会习惯您喜欢的风格,在遇到外部输入错误时过早退出

在异常处理和自动资源管理(例如:没有析构函数的语言或类似于GC的C语言)出现之前,尝试将函数出口移到作用域底部的想法非常有用,因为错误恢复通常需要手动清理

在这些手动清理的情况下,将出口移到函数的底部通常是有用的,这样您就可以查看函数顶部创建函数所需临时资源的逻辑,并可以看到函数底部对称清理这些资源

在诸如assembly之类的情况下,很常见的情况是,
跳转/分支到函数底部的错误标签,在该标签处将进行清理。即使在C语言中,使用
gotos
来实现这一目的也不太常见

此外,如前所述,深度嵌套会带来大量的心理开销。你的大脑必须像一个嵌套很深的堆栈一样工作,试图记住你在哪里,就像一个死硬的C程序员Linus Torvalds喜欢说的那样:如果你在一个函数中需要4个嵌套的缩进级别,那么你的代码已经被破坏了,应该重构(关于确切的数字,我不确定我是否同意他的观点,但他有一个观点,那就是它是如何混淆逻辑的)

<> P>当您移动到C++这样一种更现代的语言时,您现在可以通过析构函数实现自动化的资源管理。函数不再应该提到清理细节,因为资源应该通过符合所谓的资源获取是初始化习惯(不完全是最佳名称)来自动处理清理。。这就消除了支持一种力求将错误处理逻辑置于底层的风格的主要原因之一

在此之上,当你使用一种C++语言时,它可能会抛出异常并遍及整个地方。所以,每一行代码都有一个隐藏的、隐含的退出,这种逻辑的逻辑效果是不寻常的:

if an exception occurs:
    automatically cleanup resources and propagate the error
因此,到处都有隐藏的、过早的退出。因此,如果你使用这样的语言,你不仅应该习惯在出现异常时过早退出,而且还应该被迫退出,别无选择。就这些语言的可读性/可追溯性而言,最简单的方法莫过于:

if something bad happened:
     return error
我建议该规则的一个例外是静态分支预测。如果您正在编写非常关键的代码,其中最小的微效率比可读性更重要,那么您希望您的分支按照Intel的建议偏向于常见的执行行。因此,不要:

if something exceptional happened:
    return error
…为了提高性能,您可以反转逻辑并执行以下操作:

if something normal happened:
     ...
     return success
return error

这段引用自CodeComplete的话在区分多次成功返回和多次返回时更有意义,谢谢。
if something normal happened:
     ...
     return success
return error