简体   繁体   English

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

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

I had a disagreement with another programmer on how to write a method with a lot of error checking: 我与另一位程序员在如何编写带有大量错误检查的方法上存在分歧:

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
}

To me, the deep nesting of if statements makes the first example hard to read. 对我而言, if语句的深层嵌套使第一个示例难以阅读。
The second one, despite having three return s, is more readable. 尽管有三个return s,第二个更具可读性。

I checked by curiosity what Code Complete was saying about that and it left me less sure about the way to handle this: 我好奇地检查了Code Complete在说些什么,这使我不太确定如何处理此问题:

The stack of error conditions at the bottom of the nest is a sign of well-written error-processing code. 嵌套底部的错误条件堆栈是编写正确的错误处理代码的标志。

but then 但是之后

Indenting the main body of the routine inside four if statements is aesthetically ugly, especially if there's much code inside the innermost if statement. 在四个if语句中缩进例程的主体在外观上很难看,特别是如果最里面的if语句中有很多代码。

and considering using guard clauses, as in the second example 并考虑使用保护子句,如第二个示例

Minimize the number of returns in each routine. 最小化每个例程中的返回数。 It's harder to understand a routine when, reading it at the bottom, you're unaware of the possibility that it returned some-where above. 当在底部阅读该例程时,您不知道该例程是否有返回上面某个位置的可能性时,很难理解该例程。

How do you write the error checking part of a method to make it readable and error-prone? 您如何编写方法的错误检查部分以使其易读且容易出错?

Nothing gets programmers into a fight faster than stylistic debates ( Is it ever advantageous to use 'goto' in a language that supports loops and functions? If so, why? ). 没有什么比风格上的辩论更快地使程序员参与战斗了( 在支持循环和功能的语言中使用“ goto”是否有优势?如果是,为什么呢? )。 So the short answer is "Whatever style you and your team decide is best for your project/language." 因此,简短的答案是“您和您的团队决定采用哪种样式最适合您的项目/语言。”

That being said, I would like to add my 2 cents to Code Complete's comment about multiple returns. 话虽如此,我想在Code Complete关于多次收益的评论中加上2美分。 You should make a distinction between multiple successful returns, and multiple returns. 您应该区分多个成功的回报和多个回报。 If I need to investigate 5 returns that are not due to errors being generated, the function probably needs to be rewritten. 如果我需要调查不是由于错误生成的5个返回,则可能需要重写该函数。 If you gracefully exit your function immediately after an error is detected, then a maintenance programmer (ie you in 6 months) should have no more trouble following the main logic of your function than if you had nested all of those error checks. 如果您在检测到错误后立即退出函数,那么维护程序员(即6个月内)在遵循函数的主要逻辑方面应该比嵌套所有这些错误检查没有更多麻烦。

So, my personal opinion is that your second code example is the better choice. 因此,我个人认为您的第二个代码示例是更好的选择。

This is my opinion. 这是我的意见。

The old mantra of "Minimize the number of returns in each routine" seem to be a bit dated. “减少每个例程的返回次数”这一古老口号似乎有些过时了。 It is highly applicable when you have methods longer 8-10 lines of code, where lots of operations are executed. 当您的方法具有8-10行以上的代码,并且其中执行大量操作时,此方法非常适用。

The newer schools of thought, emphasizing Single Responsibility and very short methods, would seem to render that a bit unnecessary. 强调单一责任和非常简短的方法的较新的思想流派似乎使这有点不必要。 When your whole method does not do any operations directly, but simply handles the error processing, multiple returns in a clean format would be best. 当您的整个方法不直接进行任何操作,而仅处理错误处理时,最好使用纯净格式的多次返回。

In either case, any time you have nested ifs, the readable suffers substantially. 无论哪种情况,无论何时嵌套ifs,可读性都会受到很大影响。

The one optimization I would make is to use an if-else-if structure, to clearly indicate the logic flow. 我要进行的一种优化是使用if-else-if结构,以清楚地指示逻辑流程。

Sample code: 样例代码:

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

private void PerformAction()
{
    //DO STUFF
}

If you're using a language with exception-handling and automated resource management, your colleagues should probably get used to your preferred style with premature exits in the case of encountering a external input error. 如果您使用的语言具有异常处理和自动资源管理功能,那么在遇到外部输入错误的情况下,您的同事可能应该习惯于使用首选样式并过早退出。

The idea of trying to shift function exits towards the bottom of the scope was useful in the days before exception handling and automated resource management (ex: languages without destructors or GC like C) because error recovery often required manual cleanup . 在异常处理和自动资源管理(例如:没有析构函数的语言或GC之类的GC)之前,尝试将函数出口移到作用域底部的想法非常有用,因为错误恢复通常需要手动清除

In those manual cleanup cases, it was often useful to shift the exits towards the bottom of a function so that you could look at the top of the function for the logic creating the temporary resources needed by the function and towards the bottom of the function to see the symmetrical clean up of those resources. 在那些手动清理的情况下,将出口移至函数底部通常很有用,这样您就可以在函数的顶部查看逻辑,以创建函数所需的临时资源,并向函数底部移动。看到这些资源的对称清理。

In such cases as with assembly, it's quite common to see jumps/branches to an error label at the bottom of the function where the clean up would occur. 在诸如汇编的情况下,很常见的是在函数底部会出现清除的jumps/branches到错误标签。 It's also not too uncommon even in C using gotos for this purpose. 即使在为此目的而使用gotos C语言中,这种情况也并不罕见。

Also, as mentioned, deep nesting introduces a lot of mental overhead. 同样,如前所述,深度嵌套会带来很多精神上的负担。 Your brain has to function like a deeply-nested stack trying to remember where you're at, and as even Linus Torvalds, a diehard C coder, likes to say: if you need something like 4 nested levels of indentation in a single function, your code is already broken and should be refactored (I'm not sure I agree with him about the precise number, but he has a point in terms of how it obfuscates logic). 您的大脑必须像深度嵌套的堆栈一样工作,以记住您所在的位置,甚至顽固的C程序员Linus Torvalds都喜欢说:如果您在单个函数中需要4个嵌套的缩进级别,您的代码已经损坏,应该进行重构(我不确定我是否同意他的确切数字,但是他在混淆逻辑方面有其道理)。

When you move into a more modern language like C++, you now have automated resource management via destructors. 当您使用C ++等更现代的语言时,现在可以通过析构函数进行自动资源管理。 Functions should then no longer be mentioning cleanup details, as the resources should handle the cleanup automatically by conforming to what's called the resource acquisition is initialization idiom (not exactly the best name). 然后,函数不应再提及清除细节,因为资源应通过遵循所谓的资源获取即初始化习惯用法(并非最佳名称)来自动处理清除。 That eliminates one of the big reasons to favor a style that strives to have error handling logic towards the bottom. 这消除了偏向于努力使错误处理逻辑趋于底层的样式的重要原因之一。

On top of that, when you use a language like C++, it potentially throws exceptions and all over the place. 最重要的是,当您使用C ++之类的语言时,它可能会引发异常,并且到处都是。 So it's not uncommon for every other line of code to have the effect of having a hidden, implicit exit with logic like this: 因此,每隔一行代码具有如下所示的隐藏,隐式退出的效果并不少见:

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

So there are hidden, premature exits all over the place. 因此,到处都有隐藏的,过早的出口。 So if you use a language like that, not only should you get used to premature exits in the case of an exception, but you're kind of forced into it and have no other choice. 因此,如果您使用这样的语言,则不仅会在出现异常情况时习惯于过早退出,而且还被迫陷入其中而别无选择。 As far as readability/traceability is concerned in those languages, you can't get any simpler than: 就这些语言的可读性/可追溯性而言,没有比这更简单的了:

if something bad happened:
     return error

The one exception I'd suggest to the rule is static branch prediction. 我建议该规则的一个例外是静态分支预测。 If you're writing very performance-critical code where the smallest of micro-efficiencies counts more than readability, then you want your branches to be weighted towards favoring the common case line of execution as Intel advises. 如果您要编写对性能至关重要的代码,那么微效率中的最小数量比可读性更重要,那么您就希望您的分支机构能够像英特尔建议的那样,偏向于支持普通情况下的执行力。 So instead of: 所以代替:

if something exceptional happened:
    return error

... for performance you might invert the logic and do this instead: ...为了提高性能,您可以反转逻辑并改为执行以下操作:

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

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

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