Проблема и ее решение
Переключатель / регистр — это общая структура управления, реализованная в большинстве обязательных языков программирования. Переключатель считается более читабельным, чем серия 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)); } |
Вот список основных проблем в этом коде:
- Связь между литералами
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, являются их собственными. |