Как удалить большую цепочку if-else-if
Возможный дубликат:
Длинный список операторов if в Java
Мне было поручено работать с некоторым кодом, и есть гигантская цепочка if-else-if (100+ else-ifs), которая проверяет строки.
Каковы некоторые хорошие методы для обновления этого кода относительно того, где цепочка if-else-if может быть сведена к чему-то более управляемому.
Цепочка выглядит примерно так:
if(name.equals("abc")){
do something
} else if(name.equals("xyz")){
do something different
} else if(name.equals("mno")){
do something different
} ......
.....
else{
error
}
Ответы
Ответ 1
Вы можете извлечь код в каждой ветки по отдельному методу, а затем перевести методы в реализации общего базового интерфейса (позвольте называть его Handler
). После этого вы можете заполнить Map<String, Handler>
и просто посмотреть и выполнить правый обработчик для заданной строки.
К сожалению, для реализации более 100 подклассов для интерфейса требуется довольно много шаблонных кодов, но в настоящее время в Java нет более простого способа для достижения этого. Реализация случаев как элементов Enum
может несколько помочь - вот пример. Идеальное решение будет использовать закрытие /lambdas, но, увы, нам нужно подождать до Java 8 для этого...
Ответ 2
С помощью Enums вы можете иметь метод для каждого экземпляра.
public enum ActionEnum {
ABC {
@Override
void doSomething() {
System.out.println("Doing something for ABC");
}
},
XYZ {
@Override
void doSomething() {
System.out.println("Doing something for XYZ");
}
};
abstract void doSomething();
}
public class MyActionClass {
public void myMethod(String name) {
ActionEnum.valueOf("ABC").doSomething();
}
}
Это все еще беспорядочно (большое перечисление с более чем 100 позициями, даже все, что он делает, это отправка), но может избежать кода инициализации HashMap (на мой взгляд, 100+ puts тоже беспорядочно).
И еще один вариант (для целей документации) был бы отражением:
public interface Action {
void doSomething();
}
public class ABCAction implements Action {
@Override
public void doSomething() {
System.out.println("Doing something for ABC");
}
}
public class MyActionClass {
void doSomethingWithReflection(String name) {
try {
Class<? extends Action> actionClass = Class.
forName("actpck."+ name + "Action").asSubclass(Action.class);
Action a = actionClass.newInstance();
a.doSomething();
} catch (Exception e) {
// TODO Catch exceptions individually and do something useful.
e.printStackTrace();
}
}
}
Каждый подход имеет компромисс:
- HashMap = Fast + Kinda messy ( "установочный" код с сотней puts)
- Enum = Fast + Kinda messy 2 (огромный файл).
- Отражение = медленнее + ошибка времени выполнения, но обеспечивает чистое разделение, не прибегая к неуклюжей большой HashMap.
Ответ 3
Некоторые варианты/идеи:
- Оставьте его как есть - он не является принципиально нарушенным и достаточно прост в обслуживании
- Использовать оператор switch (если вы используете Java 7) - не уверен, что это сильно вас впечатляет, но
- Создать HashMap строки для объектов FunctionObjects, где объекты функции реализуют требуемое поведение как метод. Тогда ваш код вызова просто:
hashMap.get(name).doSomething();
- Разбить его на иерархию вызовов функций, разделив строки. Вы можете сделать это, взяв каждую букву поочередно, так что одна ветка обрабатывает все имена, начинающиеся с "a" и т.д.
- Рефакторинг, чтобы вы не передавали имя как String, но вместо этого передавали именованный объект. Тогда вы можете просто сделать
namedObject.doSomething()
Ответ 4
Как сказал Мэтт Болл в своем комментарии, вы можете использовать шаблон команды. Определить набор классов Runnable:
Runnable task1 = new Runnable() {
public void run() { /* do something */ }
};
Runnable task2 = // etc.
Затем вы можете использовать карту из ваших ключей в runnables:
Map<String,Runnable> taskMap = new HashMap<String,Runnable>();
taskMap.put("abc", task1);
taskMap.put("xyz", task2);
// etc.
Наконец, замените цепочку if-else следующим образом:
Runnable task = taskMap.get(name);
if (task != null) {
task.run();
} else {
// default else action from your original chain
}
Ответ 5
вы можете использовать оператор switch, но операторы Switch с примерами String были реализованы в Java SE 7
лучшим решением является использование шаблона
Ответ 6
Это популярный Arrow Anti-Pattern, и Джефф обсуждает некоторые подходы, чтобы справиться с этим очень хорошо в своем сообщении здесь.