Home > Программирование > Избегайте switch! Используйте enum!

Избегайте switch! Используйте enum!

Май 26th, 2011

Источник

Недавно мне пришлось проводить рефакторинг некоторого кода и я заметил, что большая часть кода состояла из структур switch-case или if-else каскадов. И тогда я вспомнил статью Дэниэла и решил воспользоваться его четырьмя правилами для того чтобы избавиться от этого безобразия. Допустим у нас есть конструкции вида:

switch (value) {
case SOME_CONSTANT:
//do something
break;
case SOME_OTHER_CONSTANT:
//do something else
break;
...
default:
//do something totally different
break;
}

или эквивалентные if-else каскады.

Во первых давайте предположим, что константы использованные выше это некоторые перечисления. Например:

public enum Status {
  ACTIVE,
  INACTIVE,
  UNKNOWN;
}

Аналогичный switch-case выглядел бы таким образом:

switch (getState()) {
  case INACTIVE:
    //do something
    break;
  case ACTIVE:
    //do something else
    break;
  case UNKNOWN:
    //do something totally different
    break;
}

В этом случае нам вообще не нужны switch-case. Мы можем сделать все использовав только перечисление.

public enum Status {
  INACTIVE {
    public void doSomething() {
      //do something
    }
  },
  ACTIVE {
    public void doSomething() {
      //do something else
    }
  },
  UNKNOWN {
    public void doSomething() {
      //do something totally different
    }
  };

  public abstract void doSomething();
}

И тогда switch-case сократиться до:

getState().doSomething();

Но что делать если константы определенны в чужом коде. Давайте оптимизируем наш код для этого случая.

public static final int INACTIVE = 0;
public static final int ACTIVE = 1;
public static final int UNKNOWN = 2;

Кажется мы вернулись туда откуда пришли. А вот и нет. Все что нам нужно это сделать так:

Status.values()[getState()].doSomething();

В этом случае есть некоторые проблемы на которые вы должны обратить внимание. Enum.values () возвращает элементы массива в том порядке в котором они были определены. Поэтому убедитесь, что порядок определения соответствует каждой из констант. Кроме того убедитесь, что вы не солкнетесь с исключением ArrayOutOfBoundsException. Подсказка: это еще один повод не зыбыть добавить тесты.

Существует еще один случай, что может произойти. Давайте представим, что константы определенны не в строгом порядке, как мы это сделали ранее:

public static final int INACTIVE = 4;
public static final int ACTIVE = 7;
public static final int UNKNOWN = 12;

Для покрытия этого случая нам нужно видоизменить перечисления примерно так:

public enum Status {
  INACTIVE(4),
  ACTIVE(7),
  UNKNOWN(12);

  private int state;

  public static Status getStatusFor(int desired) {
    for (Status status : values()) {
      if (desired == status.state) {
        return status;
      }
    }
    //perform error handling here
    //e.g. throw an exception or return UNKNOWN
  }
}

Даже если это включает один if, (нарушается правило №4), то все ранво это выглядит намного лучше, чем нагромождение switch-case или if-else каскадов. Подсказка: не забываем про тесты.

Как вы относитесь к этой технике? Применяли ли вы её когда-либо в своей работе и каких результатов добивались?

, , , ,

  1. No comments yet.