6

Assume a method chooses an action depending on a value from a fairly large enum. Our Sonar now complains about a high cyclomatic complexity (of about the number of case statements, naturally) of this method.

I know, large switch case statements are not really best style in OOP, but sometimes it is quite opportune to use them (in my case a parser evaluating operator tokens) instead of building a complex object tree.

My concern now how to deal with that? Is there any design pattern to split such a switch case meaningfully? Or can I (and should I) exclude the class from measuring CC (as there may be other methods in it where a high CC can easily be avoided)?

It is not a really a critical thing; I just dislike my project having warnings that I can't remove ;o)

Edit: code sample

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;
}
Uwe Allner
  • 3,399
  • 9
  • 35
  • 49

3 Answers3

7

Instead of using a large switch statement you can use the enums to build a state machine. What you do is take the code to parse the text in each case block, and put this in a method for each of the enum states.

From example

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));
}

From Using an enum as a State Machine

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • That seems to be a very interesting approach; but I have some difficulties to adapt it to my situation (see added code sample above), where some of the tokens/enum members lead to the same results. And assume that there is more than one such switch statement in different contexts... – Uwe Allner May 23 '14 at 08:28
  • @UweAllner In that case you can have an enum for each type of action and associate that enum with your `op` enum. – Peter Lawrey May 23 '14 at 08:39
  • 2
    +1, although you might get another Sonar violation about that empty while statement at the end of the process method :-) – matt freake May 23 '14 at 08:46
3

I would suggest replacing a big switch with a hashmap/dictionary, and a command object :

Map<Op, Command> ops = new EnumMap<op>{{
  //initialize with different command objects implementing same interface
}};
command = ops.get(result);
result = command.prepare();
NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • This approach is also interesting, especially perhaps for **very** large enums. Sadly I cannot accept two answers here, but thanks anyway ;o) – Uwe Allner May 23 '14 at 08:55
1

You could move your logic to command classes with a shared interface (MyCommandInterface in exammple) and make make a mapping between the enum value and a command in the enum.

MyCommand implements the MyCommandInterface (Edited)

public enum MyEnum {

  MyVALUE(new MyCommand()), MyVALUE2(new MyCommand());

  private MyCommandInterface command;

  public MyCommandInterface getCommand() {
     return command;
  }

  private MyEnum(MyCommandInterface command) {
    this.command = command;
  }
}
azarai
  • 161
  • 6