【问题标题】:Handle multiple "else if" statements. Reduce cyclomatic complexity [closed]处理多个“else if”语句。降低圈复杂度
【发布时间】:2019-09-03 14:29:53
【问题描述】:

如果不绑定到一个变量,处理多个条件情况的最佳做法是什么?

例如我们可以使用else if 声明:

if (a && b > 10) {
    ...
} else if (a && b < 5) {
    ...
} else if (!a && c === 'test') {
    ....
} else if (!a && c === 'test2') {
    ...
} else {
    ...
}

这只是一个例子,所以不要试图通过重新组合逻辑来简化它。请注意,我们不能在这里使用简单的开关。我们也不能使用带有选项的地图。 作为替代方案,我们可以使用switch(true) 使其更具可读性:

switch(true) {
    case (a && b > 10):
        ...
        break;
    case (a && b < 5):
        ...
        break;
    case (!a && c === 'test'):
        ...
        break;
    case (!a && c === 'test2'):
        ...
        break;
    default:
        ...
}

这是绝对合法的,并得到许多作者的推荐。但另一方面,许多开发人员不喜欢它,因为 true 语句不是变量而是常量。

我读过一些关于switch(true) 用法的帖子。如下:javascript switch(true)

在没有一个变量的情况下,对于这样的多个条件,最好的解决方案是什么?

解决方案: 我最终得到了 Erik Philips 答案的简化版本。谢谢埃里克!

let descriptionByType: {description: string, check: check: () => Boolean}[] = [
    { description: 'result 1', check: () => a && b > 10 },
    { description: 'result 2', check: () => a && b < 5 },
    { description: 'result 3', check: () => !a && c === 'test' },
    { description: 'result 4', check: () => !a && c === 'test2' },
    { description: 'default result', check: () => true },
];
return descriptionByType.find(x => x.check()).description;

它非常清晰和灵活。

【问题讨论】:

  • 我会说,这是一个品味问题。我以前从未见过switch (true) 语法,实际上我认为这是 switch 语句的“怪异”用法,因为它很可能不打算以这种方式使用。我的偏好是坚持使用if-else if-else 块。大多数开发工具也能够很好地折叠块,因此,如果您需要概览,您可以通过这种方式获得它。 switch 块折叠支持可能要少得多。
  • 是的,switch(true) 绝对可怕,需要你处理失败。它比你意思if/else链可读性差,并且欺骗了读者。
  • 顺便说一句,你可以做if (a) { if (b &gt; 10) { … } else if (b &lt; 5) { … } } else { if (c == 'test) { … } else if (c == 'test2') { … } }(如果你看到优势,你可以在cswitch
  • Christoph Herold,我们可以用花括号将箱体包裹起来,然后就可以折叠了。如果箱体很大,这很有用。
  • 这似乎是一个意见问题或链接问题的完全相同。我们应该在这里做什么?

标签: javascript typescript if-statement switch-statement


【解决方案1】:

if 语句/switch 逻辑过多会导致测试和Cyclomatic Complexity 出现问题。

对此普遍接受的解决方案是将您的逻辑连同与之关联的函数一起存储在列表/字典中(不是一个完美的例子):

public class LogicalFunc
{
  Logic: () => boolean;
  Func: () => void;
}

const logicalFunctions: LogicalFunc[] = [
  new LogicalFunc {
    Logic: (a: number, b: number) => (a && b) > 10,
    Func: () => someOtherFunc(),
  }
];

function myFunction(a: number, b: number) {

  const logicalFunc = logicalFunctions.find(lf => lf.Logic(a,b));
  if (logicalFunc) {
    logicalFunc.Func();
  } else {
    // some default
  }
}

function someOtherFunc() {}

现在您的逻辑测试与方法是分开的,这意味着您可以独立于myFunction 来测试列表。您还可以覆盖列表以测试myFunction 仅有的两种可能结果,即找到或未找到项目。被覆盖的肯定列表是:

  new LogicalFunc {
    Logic: (a: number, b: number) => true,
    Func: () => void(),
  }

而否定的列表将是一个空列表。

Eliminating Cylclomatic Complexity by replacing switch/case with a method or a Dictionary<TKey, func<>> (c#)

Refactoring Switch Statements To Reduce Cyclomatic Complexity (c#)

Cyclomatic complexity refactoring tips for javascript developers

No ifs…alternatives to statement branching in JavaScript

【讨论】:

  • 是的,看起来确实令人印象深刻。应该是很好的解决方案,尤其是对于大量案例。我以前从未见过这样的实现。能否提供一些阅读链接?
  • 添加了替代/更好描述的链接。
【解决方案2】:

我相信您知道,这里确实没有正确的答案。任何更具可读性的都是最好的选择。我认为您会发现其他开发人员会更乐意阅读 if、else 链而不是布尔 switch 语句(有时间和地点)。鉴于您的示例,我会尝试将其分解为更一般的陈述,然后从那里深入挖掘。例如,而不是这样:

if (a && b > 10) {
    ...
} else if (a && b < 5) {
    ...
} else if (!a && c === 'test') {
    ...
} else if (!a && c === 'test2') {
    ...
} else {
    ...
}

也许,这个:

if (a) {
    if (b > 10) {
        ...
    } else if (b < 5) {
        ...
    }
} else {
    if (c === 'test') {
        ...
    } else if (c === 'test2') {
        ...
    }
}

在您的情况下,这并不完全有效,因为该解决方案不适应链中的最终 else 语句,但我的观点是,在大多数情况下,具有复杂逻辑的 if、else 链具有简单的布尔语句并且尽可能线性。在我看来,除非你真的需要,否则不需要涉及 switch 语句。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2020-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多