【问题标题】:Does using a map truly reduce cyclomatic complexity?使用地图真的可以降低圈复杂度吗?
【发布时间】:2023-12-04 11:55:01
【问题描述】:

假设我有下面的原始方法。

public String someMethod(String str) {
    String returnStr;
    if("BLAH".equals(str)) { 
       returnStr="ok";
    } else if ("BLING".equals(str)) {
       returnStr="not ok";
    } else if ("BONG".equals(str)) {
       returnStr="ok";
    }
    return returnStr;
}

转成以下真的会降低CC吗?

Map<String, String> validator = new HashMap<String,String>();
validator.put("BLAH","ok");
validator.put("BLING","not ok");
validator.put("BONG","ok");

public String someMethod(String str) {
  return validator.get(str);
}

【问题讨论】:

  • 现在使用 20 个值。
  • 在研究后我发现了一个博客,它讨论了与我在下面的答案中讨论的相同方法。博客地址是sourcemaking.com/refactoring/…

标签: java testing static-analysis code-metrics


【解决方案1】:

是的,在你的情况下。简单来说,圈复杂度是从起点到达代码片段末尾的多种线性无关方式。因此,任何条件运算符都会增加代码的 CC。

(如果 OP 的问题与 testing 标记有关) 但是,减少 CC 并不会减少必须编写以覆盖您的代码的单元测试的数量:CC 只给您一个测试计数的下限。为了获得良好的覆盖率,单元测试应该涵盖所有特定案例,而在第二种情况下,您不会减少特定案例的数量,只会使您的代码更具可读性。

【讨论】:

    【解决方案2】:

    是的,因为圈复杂度定义了控制流图中线性独立路径的数量加一。在您的第二个示例中,只有一个路径,第一个路径有多个通过 if 分支的路径。但是,这里的圈复杂度似乎并不是真正的问题。您可以像这样替换您的方法,以使其更具可读性:

    public String someMethod(String str) {
    
        switch(str) {
            case "BLAH":
            case "BONG": return "ok";
            case "BLING": return "not ok";
            default: return null;
        }
    
    }
    

    【讨论】:

    • Switch 语句确实使它难以阅读,但仍然存在我们遇到的所有维护问题,如果使用 switch 案例或多或少地它们仍未解决。
    • 我认为在这个简单的例子中它更容易阅读,但是我同意你的观点,维护仍然是一个问题。
    • 是的,但前提是我们确定我们有已修复且不会经常更改的案例。
    • 虽然 swich 语句确实有帮助,但它是在静态分析中发现的一个问题,它是关于减少 cc。
    【解决方案3】:

    简短回答:是的,在您的案例中使用 Hashmap 确实降低了 Cyclomatic complexity。

    详细答案:根据*的圈复杂度是

    它是通过程序源代码的线性独立路径数量的定量度量。

    有多种方法可以处理 if-else 情况。 If-else 语句使代码的可读性降低,难以理解。这些 if-else 也很糟糕,因为每次您在案例中添加/删除/修改时,您都需要修改现有代码文件,而您的其他业务逻辑保持不变,并且由于这些文件的更改,您需要全部测试它们再次。这会导致维护问题以及我们需要确保处理这些案例的所有地方。 switch 语句也存在同样的问题,尽管它们更具可读性。

    您使用的方式也减少了不同的执行逻辑路径。另一种替代方法如下。

    你可以创建一个接口说IPair。让这个接口定义一个抽象方法public String getValue(); 让我们为每种情况定义不同的类,让BlahMatch.java, Bling.java and Bong.java 实现IPair 并在getValue() 方法的实现中返回适当的String

    public String someMethod(IPair pair) {
        return pair.getValue();
    }
    

    上述方法的优点是,如果您以后有一些新的对,您仍然可以创建一个新类并轻松传递新类的对象,只需要让您的类为IPair 提供实现。

    【讨论】:

    • 所讨论的方法是干净的,并且在维护期间增强了易用性。它简要讨论了其他方法的缺点。它提供了可行的替代视角,并且是避免if else statements 的体面设计。如果投反对票,请发表评论,回复需要时间,我只是建议不要为任何帖子投稿,如果我们投反对票让至少尊重努力并发表评论,可能会在投反对票时发生,我们自己也不清楚。为了便于维护,互联网上有很多资料,观看 uncle bob 视频,阅读 GOF 设计模式。照顾好同样重要。
    最近更新 更多