Подтвердить что ты не робот

Как удалить большую цепочку 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
}
4b9b3361

Ответ 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, и Джефф обсуждает некоторые подходы, чтобы справиться с этим очень хорошо в своем сообщении здесь.