具有许多条件返回语句的重构方法

Posted

技术标签:

【中文标题】具有许多条件返回语句的重构方法【英文标题】:Refactoring method with many conditional return statements 【发布时间】:2010-03-02 20:12:38 【问题描述】:

我有一个包含许多条件语句的验证方法。基本上就可以了

If Check1 = false 
  return false
If Check2 = false
  return false
etc

FxCop 抱怨圈复杂度太高。我知道在函数中间有 return 语句不是最佳实践,但同时我看到的唯一选择是一个丑陋的 If-else 语句列表。解决这个问题的最佳方法是什么?

提前致谢。

【问题讨论】:

【参考方案1】:

我不同意您的说法,即在方法中间使用 return 语句不是最佳做法。有些人为了有一个单一的 return 语句而付出的努力是疯狂的——使用任何产生最易读代码的东西。有时这将是一个单点返回,但我经常发现有一个“提前退出” - 拥有该返回比使用 if 为替代路径引入更多嵌套代码更好。根据经验,我喜欢不会缩进太远的方法:)

话虽如此,该方法真的是 nothing 而是检查吗?检查是独立的吗?他们需要什么变量?你能把它们分成更小的方法来代表你正在执行的检查的“区域”吗?

【讨论】:

您可以将其想象为验证用户对许多字段的输入。有些检查是独立的,有些则不是。你是对的,如果只使用一个 return 语句,它可以引入许多嵌套的 if 语句和缩进代码。 我认为只有一个 return 语句的最佳实践来自纯 C 代码,其中手动释放资源。在这种情况下,单个返回点简化了资源处理。在带有垃圾收集的现代语言中,它不再相关,所以我完全同意上述观点。 @Anders:垃圾收集和 try/finally 或 using 语句当然可以进行更优雅的清理 - 我认为您对咒语的来源是正确的。这只是另一个了解为什么某些事物被认为是好的情况,并根据其他情况重新评估它们。如此重要。【参考方案2】:

使用您的示例:

return (Check1 == false)   ||   (Check2 == false)   [ || etc ]

【讨论】:

【参考方案3】:

对于您的特定情况,您似乎可以使用循环...假设这来自表单或其他内容中的事件处理程序:

For Each ctrl As Control In Me.Controls

    Dim check As CheckBox = TryCast(ctrl, CheckBox)

    If check IsNot Nothing AndAlso check.Checked = False Then
        Return False
    End If

Next

Return True

编辑:经过进一步审查,我意识到这是基于伪代码,您实际上并没有使用复选框。 然而,我认为同样的方法也适用。您可以很容易地将您的规则重构为一个实现自定义接口(IRule 或类似接口)的单独类,或者拥有一个返回 true/false 的委托列表,您将在检查模式中对其进行迭代。

【讨论】:

一旦条件语句开始变得更加复杂,这是正确的方法。此答案中提供的复选框示例非常聪明 - 为什么您不应该对代码运行的验证器集进行运行时控制。另一个常见的情况是,当您想要返回验证失败的原因时,最好将这种事情从实际的条件逻辑中隐藏起来。【参考方案4】:

如果您连续进行多次检查并且不需要保留短路,您可以尝试这样的操作:

        bool isValid = true;
        isValid &= Condition1;
        isValid &= Condition2;
        isValid &= Condition3;
        if (!isValid)
            return false;

如果确实需要保留短路,可以将条件合并到一个大的 if 语句中。但是,如果有很多条件,我更喜欢具有多个返回的原始代码,因为单个大的 'if' 可能会有点难看。

请注意,在后一种情况下,您只是在回避指标,因为逻辑分支实际上是相同的。如果条件检查没有副作用(我真的希望它们没有),那么我认为高圈复杂度是一种误报,因为多重返回实际上只是表达复杂布尔验证逻辑的一种更易读的方式。

这只有在条件都串联时才成立。如果它们分布在整个代码中,并且在检查之间发生有意义的处理,那么该指标表明了真正的复杂性(必须测试更多的分支)。在这种情况下,您可能会考虑是否可以在逻辑上将该方法分解为更小的、可单独测试的部分。

【讨论】:

以上是关于具有许多条件返回语句的重构方法的主要内容,如果未能解决你的问题,请参考以下文章

自定义 Eclipse 重构预览中的错误

重构手法之简化条件表达式

重构许多嵌套 if 或链式 if 语句

小酌重构系列[23]——封装条件

将 Angular 组件从许多输入/输出重构为单个配置对象

小酌重构系列[22]——尽快返回