Проблема и ее решение
Переключатель / регистр — это общая структура управления, реализованная в большинстве обязательных языков программирования. Переключатель считается более читабельным, чем серия if / else.
Вот простой пример:
|
1
2
3
4
5
6
7
|
// Switch with int literalswitch (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));} |
Вот список основных проблем в этом коде:
- Связь между литералами
int(1, 2, 3) и исполняемым кодом не очевидна. - Если одно из значений (например, 2) больше не поддерживается и этот переключатель не обновляется, соответственно, он навсегда будет содержать неиспользованный код.
- Если вводится новое возможное значение c (например, 4), а переключатель не обновляется, соответственно, код, вероятно, вызовет исключение
UnsupportedOperationExceptionво время выполнения без каких-либо уведомлений во время компиляции. - Такая структура переключателей имеет тенденцию дублироваться в коде несколько раз, что делает проблемы 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(); |
Это решение не имеет перечисленных выше недостатков:
- Это читабельно. Метод «привязан» к элементу
enum; можно написать столькоjavadocсколько нужно, если смысл метода неясен. Код, который вызывает метод, тривиален: что может быть проще, чем вызов метода? -
enumудалить константуenumбез удаления реализации, поэтому неиспользуемый код не останется, если некоторые функции больше не будут актуальны. - Новый элемент
enumнельзя добавить без реализации методаaction(). Код без реализации не может быть скомпилирован. - Если требуется несколько действий, все они могут быть реализованы в enum. Как мы уже упоминали, код, вызывающий определенную функцию, тривиален, поэтому теперь нет дублирования кода.
Вывод
Хотя структура switch / case хорошо известна и широко используется в различных языках программирования, ее использование может вызвать много проблем. Решение, которое использует перечисления java и описано выше, не имеет этих недостатков. Следующая статья из этой серии показывает, как расширить функциональность существующего enum .
|
Опубликовано на Java Code Geeks с разрешения Александра Радзина, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Избранные перечисления вместо switch Мнения, высказанные участниками Java Code Geeks, являются их собственными. |