繁体   English   中英

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

[英]How do you write the error checking part of a method to make it readable and error prone?

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

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

-

public void performAction() {     
  if (!test1) {
    return "error 1";
  }
  if (!test2) {
    return "error 1";
  }
  if (!test3) {
    return "error 1";
  }
  // DO STUFF
}

对我而言, if语句的深层嵌套使第一个示例难以阅读。
尽管有三个return s,第二个更具可读性。

我好奇地检查了Code Complete在说些什么,这使我不太确定如何处理此问题:

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

但是之后

在四个if语句中缩进例程的主体在外观上很难看,特别是如果最里面的if语句中有很多代码。

并考虑使用保护子句,如第二个示例

最小化每个例程中的返回数。 当在底部阅读该例程时,您不知道该例程是否有返回上面某个位置的可能性时,很难理解该例程。

您如何编写方法的错误检查部分以使其易读且容易出错?

没有什么比风格上的辩论更快地使程序员参与战斗了( 在支持循环和功能的语言中使用“ goto”是否有优势?如果是,为什么呢? )。 因此,简短的答案是“您和您的团队决定采用哪种样式最适合您的项目/语言。”

话虽如此,我想在Code Complete关于多次收益的评论中加上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之类的GC)之前,尝试将函数出口移到作用域底部的想法非常有用,因为错误恢复通常需要手动清除

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

在诸如汇编的情况下,很常见的是在函数底部会出现清除的jumps/branches到错误标签。 即使在为此目的而使用gotos C语言中,这种情况也并不罕见。

同样,如前所述,深度嵌套会带来很多精神上的负担。 您的大脑必须像深度嵌套的堆栈一样工作,以记住您所在的位置,甚至顽固的C程序员Linus Torvalds都喜欢说:如果您在单个函数中需要4个嵌套的缩进级别,您的代码已经损坏,应该进行重构(我不确定我是否同意他的确切数字,但是他在混淆逻辑方面有其道理)。

当您使用C ++等更现代的语言时,现在可以通过析构函数进行自动资源管理。 然后,函数不应再提及清除细节,因为资源应通过遵循所谓的资源获取即初始化习惯用法(并非最佳名称)来自动处理清除。 这消除了偏向于努力使错误处理逻辑趋于底层的样式的重要原因之一。

最重要的是,当您使用C ++之类的语言时,它可能会引发异常,并且到处都是。 因此,每隔一行代码具有如下所示的隐藏,隐式退出的效果并不少见:

if an exception occurs:
    automatically cleanup resources and propagate the error

因此,到处都有隐藏的,过早的出口。 因此,如果您使用这样的语言,则不仅会在出现异常情况时习惯于过早退出,而且还被迫陷入其中而别无选择。 就这些语言的可读性/可追溯性而言,没有比这更简单的了:

if something bad happened:
     return error

我建议该规则的一个例外是静态分支预测。 如果您要编写对性能至关重要的代码,那么微效率中的最小数量比可读性更重要,那么您就希望您的分支机构能够像英特尔建议的那样,偏向于支持普通情况下的执行力。 所以代替:

if something exceptional happened:
    return error

...为了提高性能,您可以反转逻辑并改为执行以下操作:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM