【问题标题】:Can high cyclomatic complexity (warnings) be avoided when using switch-case on a large enum?在大型枚举上使用 switch-case 时可以避免高圈复杂度(警告)吗?
【发布时间】:2014-05-23 08:16:12
【问题描述】:

假设一个方法根据一个相当大的枚举值选择一个动作。 我们的声纳现在抱怨这种方法的圈复杂度很高(自然是关于 case 语句的数量)。

我知道,大型 switch case 语句并不是 OOP 中最好的样式,但有时使用它们(在我的例子中是评估运算符标记的解析器)而不是构建复杂的对象树是非常合适的。

我现在关心的是如何处理?是否有任何设计模式可以有意义地拆分这样的开关盒?或者我可以(并且应该)从测量 CC 中排除该类(因为其中可能有其他方法可以轻松避免高 CC)?

这并不是一件真正重要的事情;我只是不喜欢我的项目有我无法删除的警告;o)

编辑:代码示例

String process()
    String fieldName = this.getField() != null ? this.getField().getSchemaName() : null;
    String result = "";
    switch (op) {
    case PHRASE:
        result = "";
        if(!value.isEmpty() && value.get(0) != null) {
            result = value.get(0).toString();
        }
        break;
    case EQUALS:
    case GT:
    case GTE:
    case LT:
    case LTE:
    case NOT_EQUALS:
        result = prepareSingleParameterStatement(fieldName);
        break;
    case BETWEEN_EXC:
    case BETWEEN_INC:
        result = prepareDoubleParameterStatement(fieldName);
        break;
    case IN:
    case NOT_IN:
    case ALL_IN:
        result = prepareCollectionStatement(fieldName);
        break;
    case AND:
    case OR:
        result = prepareLogicalStatement();
        break;
    case NOT:
        result = prepareNotStatement();
        break;
    default:
        break;
    }
    return result;
}

【问题讨论】:

    标签: java enums sonarqube cyclomatic-complexity


    【解决方案1】:

    您可以使用枚举来构建状态机,而不是使用大型 switch 语句。您所做的是获取代码来解析每个 case 块中的文本,并将其放入每个枚举状态的方法中。

    来自示例

    enum States implements State {
        XML {
            public boolean process(Context context) {
                if (context.buffer().remaining() < 16) return false;
                // read header
                if(headerComplete)
                    context.state(States.ROOT);
                return true;
            }
        }, ROOT {
            public boolean process(Context context) {
                if (context.buffer().remaining() < 8) return false;
                // read root tag
                if(rootComplete)
                    context.state(States.IN_ROOT);
                return true;
            }
        }
    }
    
    public void process(Context context) {
        socket.read(context.buffer());
        while(context.state().process(context));
    }
    

    来自Using an enum as a State Machine

    【讨论】:

    • 这似乎是一个非常有趣的方法;但是我在适应我的情况时遇到了一些困难(请参阅上面添加的代码示例),其中一些令牌/枚举成员会导致相同的结果。并假设在不同的上下文中存在不止一个这样的 switch 语句......
    • @UweAllner 在这种情况下,您可以为每种类型的操作设置一个枚举,并将该枚举与您的 op 枚举相关联。
    • +1,尽管您可能会在处理方法结束时收到关于该空 while 语句的另一个声纳违规 :-)
    【解决方案2】:

    我建议用哈希图/字典和命令对象替换一个大开关:

    Map<Op, Command> ops = new EnumMap<op>{{
      //initialize with different command objects implementing same interface
    }};
    command = ops.get(result);
    result = command.prepare();
    

    【讨论】:

    • 这种方法也很有趣,尤其是对于 非常 大的枚举。可悲的是我不能在这里接受两个答案,但无论如何谢谢;o)
    【解决方案3】:

    您可以将您的逻辑移动到具有共享接口的命令类(例如 MyCommandInterface),并在枚举值和枚举中的命令之间进行映射。

    MyCommand 实现了 MyCommandInterface(已编辑)

    public enum MyEnum {
    
      MyVALUE(new MyCommand()), MyVALUE2(new MyCommand());
    
      private MyCommandInterface command;
    
      public MyCommandInterface getCommand() {
         return command;
      }
    
      private MyEnum(MyCommandInterface command) {
        this.command = command;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-24
      • 2015-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多