【问题标题】:PMD - NPath complexity very high with ternary operator (?PMD - NPath 复杂性非常高,三元运算符 (?
【发布时间】:2011-02-22 15:11:59
【问题描述】:

我正在使用 PMD 为项目生成一些代码质量报告。
我不明白 NPath 复杂性检查的结果。
我创建了一个无聊的类来展示结果(这不是真正的类,但它使用相同的模式):

import java.util.*;

public class SOFExample {

    private final Map<String, Date> magicMap = new HashMap<String, Date>();    
    protected static final long UNKNWOWN = 0L;
    private static final class MyCal { long aTime; long bTime; long cTime; long dTime;}

    public void usefullMethod(final List<MyCal> myCals) {

        final Date a = magicMap.get("a");
        final Date b = magicMap.get("b");
        final Date c = magicMap.get("c");
        final Date d = magicMap.get("d");

        final long aTime = a == null ? UNKNWOWN : a.getTime();
        final long bTime = b == null ? UNKNWOWN : b.getTime();
        final long cTime = c == null ? UNKNWOWN : c.getTime();
        final long dTime = d == null ? UNKNWOWN : d.getTime();

        for (MyCal myCal : myCals) {
            if(myCal.aTime == UNKNWOWN) myCal.aTime = aTime;
            if(myCal.bTime == UNKNWOWN) myCal.bTime = bTime;
            if(myCal.cTime == UNKNWOWN) myCal.cTime = cTime;
            if(myCal.dTime == UNKNWOWN) myCal.dTime = dTime;
        }
    }
}

PMD 结果:

方法 usefullMethod() 的 NPath 复杂度为 10625

如果我添加一个以相同方式初始化的新变量,我会得到:

方法 usefullMethod() 的 NPath 复杂度为 103125

如果我全部替换呢?使用 if-else 结构,我得到了这个:

方法 usefullMethod() 的 NPath 复杂度为 1056

为什么我用三元“?”得到如此高的结果?操作员?

这段代码有什么问题? (在这个演示代码中,很容易提取一个获取默认值的方法,但在实际代码中可能无法实现)

【问题讨论】:

    标签: pmd


    【解决方案1】:

    为了让示例更简单,这个类的 nPath 值为 2。应该很明显为什么它是 2 - 显然有两个通过代码的执行路径。

    package test;
    
    import java.util.*;
    
    public class Test {
    
        private static final long UNKNWOWN = -1;
    
        public void method(Date a) {
            long aTime;
    
            if (a == null) {
                aTime = UNKNWOWN;
            } else {
                aTime = a.getTime();
            }
        }
    }
    

    并且这个类的 nPath 值为 5。问题是为什么 - 仍然有两个逻辑路径通过代码。

    package test;
    
    import java.util.*;
    
    public class Test {
    
        private static final long UNKNWOWN = -1;
    
        public void method(Date a) {
            final long aTime = a == null ? UNKNWOWN : a.getTime();
        }
    }
    

    但是,使用的algorithm如下:

    int npath = complexitySumOf(node, 0, data);     
    npath += 2;
    

    它增加了所有孩子的复杂性,然后为三元增加了两个。简单 java 节点返回的最小复杂度为 1。AbstractSyntaxTree 显示有三个子节点。因此 3 + 2 是 5。

    <ConditionalExpression beginColumn="36" beginLine="11" endColumn="69" endLine="11" ternary="true">
      <EqualityExpression beginColumn="36" beginLine="11" endColumn="44" endLine="11" image="==">
        <PrimaryExpression beginColumn="36" beginLine="11" endColumn="36" endLine="11">
           <PrimaryPrefix beginColumn="36" beginLine="11" endColumn="36" endLine="11">
             <Name beginColumn="36" beginLine="11" endColumn="36" endLine="11" image="a"/>
           </PrimaryPrefix>
        </PrimaryExpression>
        <PrimaryExpression beginColumn="41" beginLine="11" endColumn="44" endLine="11">
          <PrimaryPrefix beginColumn="41" beginLine="11" endColumn="44" endLine="11">
            <Literal beginColumn="41" beginLine="11" charLiteral="false" endColumn="44" endLine="11" floatLiteral="false" intLiteral="false" singleCharacterStringLiteral="false" stringLiteral="false">
              <NullLiteral beginColumn="41" beginLine="11" endColumn="44" endLine="11"/>
           </Literal>
          </PrimaryPrefix>
        </PrimaryExpression>
      </EqualityExpression>
      <Expression beginColumn="48" beginLine="11" endColumn="55" endLine="11">
        <PrimaryExpression beginColumn="48" beginLine="11" endColumn="55" endLine="11">
          <PrimaryPrefix beginColumn="48" beginLine="11" endColumn="55" endLine="11">
            <Name beginColumn="48" beginLine="11" endColumn="55" endLine="11" image="UNKNWOWN"/>
          </PrimaryPrefix>
         </PrimaryExpression>
      </Expression>
      <PrimaryExpression beginColumn="59" beginLine="11" endColumn="69" endLine="11">
        <PrimaryPrefix beginColumn="59" beginLine="11" endColumn="67" endLine="11">
          <Name beginColumn="59" beginLine="11" endColumn="67" endLine="11" image="a.getTime"/>
        </PrimaryPrefix>
        <PrimarySuffix argumentCount="0" arguments="true" arrayDereference="false" beginColumn="68" beginLine="11" endColumn="69" endLine="11">
          <Arguments argumentCount="0" beginColumn="68" beginLine="11" endColumn="69" endLine="11"/>
        </PrimarySuffix>
      </PrimaryExpression>
    </ConditionalExpression>
    

    如果您在三元运算符中有一个复杂的表达式,那么它所计算的差异会更加普遍。至于代码有什么问题,它已经有 9 个分支(8 个三元运算符和一个循环),即使没有整个 nPath 计算,它也很高。无论如何我都会重构它。

    【讨论】:

    • 感谢您的解释。事实上,我已经查看了代码,试图理解为什么我的代码的复杂性如此之高。我不明白的是为什么这个算法用于三元运算符,给 npath 一个对数值(100、1000、10000、1000000,...)这对于真正的复杂性来说听起来并不公平...... 关于重构,您必须处理潜在的空值这一事实导致这样的代码难以重构,除非使用复杂的模式......所以它并不总是好的解决方案......
    • 我不会评判什么是好是坏。只是说是什么。 (而且我同意他们在三元运算符的情况下多计了。)
    猜你喜欢
    • 2015-09-05
    • 1970-01-01
    • 2021-04-14
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 2016-07-19
    相关资源
    最近更新 更多