Статьи

Рекомендуемое перечисление вместо switch

Проблема и ее решение

Переключатель / регистр — это общая структура управления, реализованная в большинстве обязательных языков программирования. Переключатель считается более читабельным, чем серия if / else.

Вот простой пример:

1
2
3
4
5
6
7
// Switch with int literal
switch (c) {
  case 1: one(); break;
  case 2: two(); break;
  case 3: three(); break;
  default: throw new UnsupportedOperationException(String.format("Operation %d is not supported", c));
}

Вот список основных проблем в этом коде:

  1. Связь между литералами int (1, 2, 3) и исполняемым кодом не очевидна.
  2. Если одно из значений (например, 2) больше не поддерживается и этот переключатель не обновляется, соответственно, он навсегда будет содержать неиспользованный код.
  3. Если вводится новое возможное значение c (например, 4), а переключатель не обновляется, соответственно, код, вероятно, вызовет исключение UnsupportedOperationException во время выполнения без каких-либо уведомлений во время компиляции.
  4. Такая структура переключателей имеет тенденцию дублироваться в коде несколько раз, что делает проблемы 2 и 3 еще более сложными.

Первое простейшее исправление можно сделать, используя int-константы вместо литералов. Во-первых, давайте определим константы:

1
2
3
private static int ONE = 1;
private static int TWO = 2;
private static int THREE = 3;

Теперь код будет выглядеть так:

1
2
3
4
5
6
switch (c) {
  case ONE: one(); break;
  case TWO: two(); break;
  case THREE: three(); break;
  default: throw new UnsupportedOperationException(String.format("Operation %d is not supported", c));
}

(Очевидно, что в реальной жизни имена констант должны быть информативными)

Этот фрагмент более читабелен, но все остальные недостатки по-прежнему актуальны. Следующая попытка улучшить исходный фрагмент кода использует enums введенные в язык Java в версии 5 в 2004 году. Давайте определим следующее enum :

1
enum Action {ONE, TWO, THREE}

Теперь фрагмент переключателя будет немного изменен:

1
2
3
4
5
6
7
Action a = ...
switch (a) {
  case ONE: one(); break;
  case TWO: two(); break;
  case THREE: three(); break;
  default: throw new UnsupportedOperationException(String.format("Operation %s is not supported", a));
}

Этот код немного лучше: он выдаст ошибку компиляции, если один из элементов будет удален из enum Action . Однако это не приведет к ошибке компиляции, если в enum Action добавлен дополнительный элемент. Некоторые IDE или инструменты статического анализа кода могут выдавать предупреждение в этом случае, но кто обращает внимание на предупреждения? К счастью, enum может объявить абстрактный метод, который должен быть реализован каждым элементом:

1
2
3
4
5
6
enum Action {
  ONE { @Override public void action() { } },
  TWO { @Override public void action() { } },
  THREE { @Override public void action() { } },
  public abstract void action();
}

Теперь оператор switch можно заменить одной строкой:

1
2
Action a = ...
a.action();

Это решение не имеет перечисленных выше недостатков:

  1. Это читабельно. Метод «привязан» к элементу enum ; можно написать столько javadoc сколько нужно, если смысл метода неясен. Код, который вызывает метод, тривиален: что может быть проще, чем вызов метода?
  2. enum удалить константу enum без удаления реализации, поэтому неиспользуемый код не останется, если некоторые функции больше не будут актуальны.
  3. Новый элемент enum нельзя добавить без реализации метода action() . Код без реализации не может быть скомпилирован.
  4. Если требуется несколько действий, все они могут быть реализованы в enum. Как мы уже упоминали, код, вызывающий определенную функцию, тривиален, поэтому теперь нет дублирования кода.

Вывод

Хотя структура switch / case хорошо известна и широко используется в различных языках программирования, ее использование может вызвать много проблем. Решение, которое использует перечисления java и описано выше, не имеет этих недостатков. Следующая статья из этой серии показывает, как расширить функциональность существующего enum .

Опубликовано на Java Code Geeks с разрешения Александра Радзина, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Избранные перечисления вместо switch

Мнения, высказанные участниками Java Code Geeks, являются их собственными.