Длинный список операторов if в Java

Извините, я не могу найти ответ на этот вопрос, я почти уверен, что кто-то еще его поднял.

Моя проблема в том, что я пишу некоторые системные библиотеки для запуска встроенных устройств. У меня есть команды, которые могут быть отправлены на эти устройства по радиопередачам. Это можно сделать только по тексту. внутри системных библиотек у меня есть поток, который обрабатывает команды, которые выглядят как

if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() } 
else if etc. 

Проблема заключается в том, что есть много команд, которые быстро перевернут к чему-то из-под контроля. Ужасно смотреть, мучительно отлаживать и думать о том, чтобы понять, через несколько месяцев.

Ответ 1

с помощью Командная строка:

public interface Command {
     void exec();
}

public class CommandA() implements Command {

     void exec() {
          // ... 
     }
}

// etc etc

затем создайте объект Map<String,Command> и заполните его экземплярами Command:

commandMap.put("A", new CommandA());
commandMap.put("B", new CommandB());

то вы можете заменить цепочку if/ else if на:

commandMap.get(value).exec();

ИЗМЕНИТЬ

вы также можете добавить специальные команды, такие как UnknownCommand или NullCommand, но вам нужен CommandMap, который обрабатывает эти угловые случаи, чтобы минимизировать проверки клиентов.

Ответ 2

Ну, есть шаблон команды, но это может быть излишним для того, что вы делаете. Помните KISS.

Ответ 3

Мое предложение было бы своего рода легкой комбинацией enum и Command. Это идиома, рекомендованная Джошуа Блохом в пункте 30 эффективной Java.

public enum Command{
  A{public void doCommand(){
      // Implementation for A
    }
  },
  B{public void doCommand(){
      // Implementation for B
    }
  },
  C{public void doCommand(){
      // Implementation for C
    }
  };
  public abstract void doCommand();
}

Конечно, вы можете передавать параметры doCommand или иметь возвращаемые типы.

Это решение может быть не совсем подходящим, если реализации doCommand на самом деле не "подходят" к типу перечисления, который, как обычно, когда вам нужно сделать компромисс, немного нечеткий.

Ответ 4

Внедрение интерфейса, демонстрируемое просто и просто с помощью dfa, является чистым и элегантным (и "официально" поддерживается). Это означает концепцию интерфейса.

В С# мы могли использовать делегаты для программистов, которым нравится использовать указатели functon в c, но метод DFA - это способ использования.

У вас тоже может быть массив

Command[] commands =
{
  new CommandA(), new CommandB(), new CommandC(), ...
}

Затем вы можете выполнить команду по индексу

commands[7].exec();

Плагиат из DFA, но имеющий абстрактный базовый класс вместо интерфейса. Обратите внимание на cmdKey, который будет использоваться позже. По опыту, я понимаю, что часто у оборудования есть подкоманды.

abstract public class Command()
{
  abstract public byte exec(String subCmd);
  public String cmdKey;
  public String subCmd;
}

Создайте свои команды таким образом,

public class CommandA
extends Command
{
  public CommandA(String subCmd)
  {
    this.cmdKey = "A";
    this.subCmd = subCmd;
  }

  public byte exec()
  {
    sendWhatever(...);
    byte status = receiveWhatever(...);
    return status;
  }
}

Затем вы можете расширить общий HashMap или HashTable, предоставив функцию сосания пары ключ-значение:

public class CommandHash<String, Command>
extends HashMap<String, Command>
(
  public CommandHash<String, Command>(Command[] commands)
  {
    this.commandSucker(Command[] commands);
  }
  public commandSucker(Command[] commands)
  {
    for(Command cmd : commands)
    {
      this.put(cmd.cmdKey, cmd);
    }
  }
}

Затем создайте хранилище команд:

CommandHash commands =
  new CommandHash(
  {
    new CommandA("asdf"),
    new CommandA("qwerty"),
    new CommandB(null),
    new CommandC("hello dolly"),
    ...
  });

Теперь вы можете отправить элементы управления объективно

commands.get("A").exec();
commands.get(condition).exec();

Ответ 5

Имейте перечисление команд:

public enum Commands { A, B, C; }
...

Command command = Commands.valueOf(value);

switch (command) {
    case A: doCommandA(); break;
    case B: doCommandB(); break;
    case C: doCommandC(); break;
}

Если у вас есть несколько команд, изучите использование шаблона Command, как указано в другом месте (хотя вы можете сохранить перечисление и вставить вызов для класса реализации в enum, вместо использования HashMap). Пожалуйста, см. Ответ Андреаса или Йенса на этот вопрос для примера.

Ответ 6

Ну, я предлагаю создавать объекты команд и помещать их в хэш-карту с помощью String as Key.

Ответ 7

Даже если я полагаю, что подход с командной строкой больше подходит для лучших оценок и поддерживает в долгосрочной перспективе, здесь для вас один вариант:

org.apache.commons.beanutils.MethodUtils.invokeMethod(это, "doCommand" + значение, NULL);

Ответ 8

Я обычно пытаюсь решить его таким образом:

public enum Command {

A {void exec() {
     doCommandA();
}},

B {void exec() {
    doCommandB();
}};

abstract void exec();
 }

это имеет много преимуществ:

1) невозможно добавить перечисление без реализации exec. поэтому вы не пропустите A.

2) вам даже не придется добавлять его в любую командную карту, поэтому нет никакого шаблонного кода для построения карты. просто абстрактный метод и его реализации. (который, возможно, также является шаблоном, но он не будет короче.)

3) вы сохраните любые потраченные циклы процессора, пройдя длинный список if или вычисляя хэш-коды и выполняя поиск.

изменить: если у вас нет перечислений, кроме строк в качестве источника, просто используйте Command.valueOf(mystr).exec() для вызова метода exec. обратите внимание, что вы должны использовать общедоступный модификатор на exec, который вы хотите вызвать из другого пакета.

Ответ 9

Вам, вероятно, лучше всего использовать карту команд.

Но у вас есть набор из них, чтобы справиться с вами, в результате чего загрузилось множество карт. Тогда стоит посмотреть на это с помощью Enums.

Вы можете сделать это с помощью Enum без использования переключателей (вам, вероятно, не нужны геттеры в примере), если вы добавите метод Enum для решения для "значения". Тогда вы можете просто сделать:

Обновление: добавлена ​​статическая карта, чтобы избежать итерации при каждом вызове. Бесстыдно ущипнул от этот ответ.

Commands.getCommand(value).exec();

public interface Command {
    void exec();
}

public enum Commands {
    A("foo", new Command(){public void exec(){
        System.out.println(A.getValue());
    }}),
    B("bar", new Command(){public void exec(){
        System.out.println(B.getValue());
    }}),
    C("barry", new Command(){public void exec(){
        System.out.println(C.getValue());
    }});

    private String value;
    private Command command;
    private static Map<String, Commands> commandsMap;

    static {
        commandsMap = new HashMap<String, Commands>();
        for (Commands c : Commands.values()) {
            commandsMap.put(c.getValue(), c);    
        }
    }

    Commands(String value, Command command) {
        this.value= value;
        this.command = command;
    }

    public String getValue() {
        return value;
    }

    public Command getCommand() {
        return command;
    }

    public static Command getCommand(String value) {
        if(!commandsMap.containsKey(value)) {
            throw new RuntimeException("value not found:" + value);
        }
        return commandsMap.get(value).getCommand();
    }
}

Ответ 10

Ответ, предоставленный @dfa, является лучшим решением, на мой взгляд.

Я просто предоставляю некоторые фрагменты , если вы используете Java 8 и хотите использовать Lambdas!

Команда без параметров:

Map<String, Command> commands = new HashMap<String, Command>();
commands.put("A", () -> System.out.println("COMMAND A"));
commands.put("B", () -> System.out.println("COMMAND B"));
commands.put("C", () -> System.out.println("COMMAND C"));
commands.get(value).exec();

(вы можете использовать Runnable вместо Command, но я не считаю его семантически правильным):

Команда с одним параметром:

Если вы ожидаете параметр, вы можете использовать java.util.function.Consumer:

Map<String, Consumer<Object>> commands = new HashMap<String, Consumer<Object>>();
commands.put("A", myObj::doSomethingA);
commands.put("B", myObj::doSomethingB);
commands.put("C", myObj::doSomethingC);
commands.get(value).accept(param);

В приведенном выше примере doSomethingX - это метод, присутствующий в классе myObj, который принимает в качестве аргумента любой объект (с именем param в этом примере).

Ответ 11

если у вас есть несколько выраженных выражений if, то это шаблон для использования механизма правила. См., Например, JBOSS Drools.

Ответ 13

если было возможно иметь массив процедур (то, что вы назвали командами), которые были бы полезны.

но вы можете написать программу для написания кода. Все это очень систематично if (value = 'A') commandA(); еще если(........................ e.t.c.

Ответ 14

Я не уверен, что у вас есть какое-либо совпадение между поведением ваших различных команд, но вы также можете взглянуть на Chain Of Ответственность, который может обеспечить большую гибкость, позволяя нескольким командам обрабатывать некоторые входные значения.

Ответ 15

Если это много, тогда будет много кода, вы не можете уйти от этого. Просто проследите, дайте переменным очень значимые имена, комментарии тоже помогут...