Длинный список операторов if в Java
Извините, я не могу найти ответ на этот вопрос, я почти уверен, что кто-то еще поднял его раньше.
моя проблема заключается в том, что я пишу некоторые системные библиотеки для запуска встроенных устройств. У меня есть команды, которые могут быть отправлены в эти устройства по радио. Это можно сделать только с помощью текста. внутри системных библиотек у меня есть поток, который обрабатывает команды, которые выглядят так
if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() }
else if etc.
проблема в том, что есть много команд будет быстро спираль к чему-то из-под контроля. Ужасно смотреть, больно отлаживать и умопомрачительно понимать через несколько месяцев.
15 ответов:
используя команда:
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());
затем вы можете заменить если/иначе если цепи, с:
commandMap.get(value).exec();
EDIT
вы можете также добавить специальные команды, такие как
UnknownCommand
илиNullCommand
, а нужноCommandMap
который обрабатывает эти угловые случаи, чтобы минимизировать проверки клиента.
есть команда но это может быть излишним для того, что вы делаете. Вспомни поцелуй.
мое предложение было бы своего рода легкой комбинацией перечисления и объекта команды. Это идиома, рекомендованная Джошуа Блохом в пункте 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 действительно не" подходят " к типу перечисления, который, как обычно, когда вам нужно сделать компромисс, немного нечеткий.
есть перечисление команд:
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; }
Если у вас есть более чем несколько команд, посмотрите на использование шаблона команд, как ответили в другом месте (хотя вы можете сохранить перечисление и встроить вызов в класс реализации в перечислении вместо использования хэш-карты). Пожалуйста, смотрите ответ Андреаса или Йенса на этот вопрос для примера.
реализация интерфейса, как продемонстрировано просто и ясно dfa, является чистым и элегантным (и "официально" поддерживаемым способом). Это то, для чего предназначена концепция интерфейса.
в C# мы могли бы использовать делегаты для программистов, которые любят использовать указатели функций в 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();
Ну я предлагаю создать командные объекты и поместить их в хэш-карту, используя строку в качестве ключа.
даже если я считаю, что подход к шаблону команд больше подходит к лучшим практикам и поддерживается в долгосрочной перспективе, вот один вариант лайнера для вас:
орг.апаш.палата общин.beanutils.Методутилс.invokeMethod (this, "doCommand" +value, null);
Я обычно пытаюсь решить это таким образом:
public enum Command { A {void exec() { doCommandA(); }}, B {void exec() { doCommandB(); }}; abstract void exec(); }
это имеет много преимуществ:
1) невозможно добавить перечисление без реализации exec. так что вы не пропустите ни А.
2) вам даже не придется добавлять его в любую командную карту, поэтому нет шаблонного кода для построения карты. просто абстрактный метод и его реализации. (что, возможно, также является шаблоном, но он не станет короче..)
3) вы сэкономите лишних циклов процессора пройдя через длинный список if или вычисляя хэш-коды и выполняя поиск.
изменить: если у вас нет перечислений, но строки в качестве источника, просто используйте
Command.valueOf(mystr).exec()
для вызова метода exec. обратите внимание, что вы должны использовать модификатор public в execif, который вы хотите вызвать из другого пакета.
вам, вероятно, лучше всего использовать карту команд.
но у вас есть набор из них, чтобы справиться с вами в конечном итоге с нагрузками карт стучать. Тогда стоит посмотреть, как это делается с перечислениями.
вы можете сделать это с перечислением без использования переключателей (вам, вероятно, не нужны геттеры в примере), если вы добавите метод к перечислению для разрешения "value". Тогда вы можете просто сделать:
обновление: добавлена статическая карта, чтобы избежать итерации при каждом вызове. Бесстыдно ущипнул от ответ.
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(); } }
ответ, предоставленный @dfa, является лучшим решением, на мой взгляд.
Я просто предоставляю некоторые фрагменты в случае, если вы используете Java 8 и хотите использовать лямбда-выражения!
команда без параметров:
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 вместо команды, но я не считаю это семантически правильным):
команда с одним параметром:
в случае, если вы ожидаете параметр, который вы могли бы использовать
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
'S класс, который принимает любой объект (с именемparam
в этом примере) в качестве аргумента.
Если у вас есть несколько встроенных операторов "if", то это шаблон для использования правило. Смотрите, например JBoss пускает слюни.
Если бы можно было иметь массив процедур(то, что вы назвали командами), которые были бы полезны..
но вы могли бы написать программу, чтобы написать свой код. Это все очень систематично if (value='A') commandA (); else если.(....................... е.Т.гр.
Я не уверен, что у вас есть какие-либо перекрытия между поведением ваших различных команд, но вы также можете взглянуть на Цепи Ответственности шаблон, который может обеспечить большую гибкость, позволяя нескольким командам обрабатывать некоторые входные значения.