Java中if语句的长列表

对不起,我找不到回答这个问题,我几乎可以肯定其他人之前提出过这个问题。

我的问题是,我正在编写一些系统库来运行embedded式设备。 我有可以通过无线电广播发送到这些设备的命令。 这只能通过文字完成。 在系统库里面我有一个线程来处理看起来像这样的命令

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

问题是,有很多的命令会迅速旋转到失去控制。 可怕的看出来,痛苦的debugging,并在几个月的时间内令人难以理解。

使用命令模式 :

 public interface Command { void exec(); } public class CommandA() implements Command { void exec() { // ... } } // etc etc 

然后build立一个Map<String,Command>对象并用Command实例填充它:

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

那么你可以用ifreplaceif / else if链:

 commandMap.get(value).exec(); 

编辑

您还可以添加特殊的命令,如UnknownCommandNullCommand ,但是您需要一个处理这些angular落情况的CommandMap ,以最大限度地减less客户端的检查。

那么有一个命令模式,但它可能是矫枉过正你正在做什么。 记住KISS。

我的build议是一种枚举和Command对象的轻量级组合。 这是Joshua Bloch在Effective Java项目30中推荐的一个成语。

 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或返回types。

如果doCommand的实现并不真正“适合”enumtypes,那么这种解决scheme可能并不适用,而像往常一样,您必须进行折衷 – 有点模糊。

通过dfa简单明了地实现一个界面,干净而优雅(和“正式”支持的方式)。 这是接口概念的意义。

在C#中,我们可以为喜欢在c中使用functon指针的程序员使用委托,但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; } 

因此,构build你的命令,

 public class CommandA extends Command { public CommandA(String subCmd) { this.cmdKey = "A"; this.subCmd = subCmd; } public byte exec() { sendWhatever(...); byte status = receiveWhatever(...); return status; } } 

然后你可以通过提供一个键值对抽吸函数来扩展genericsHashMap或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); } } } 

然后构build你的命令存储:

 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(); 

有一个命令的枚举:

 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; } 

如果你有更多的命令,可以使用命令模式,就像在其他地方回答的一样(虽然你可以保留枚举并将调用embedded到枚举中的实现类中,而不是使用HashMap)。 例如,请参阅Andreas或jens对这个问题的回答。

那么我build议创build命令对象,并把它们放到一个哈希映射使用string作为关键。

即使我相信命令模式的方法更符合最佳实践,并且从长远来看仍然可以维持,下面给出一个一行的选项:

org.apache.commons.beanutils.MethodUtils.invokeMethod(这一点, “doCommand” +值,NULL);

我通常试图解决这个问题:

 public enum Command { A {void exec() { doCommandA(); }}, B {void exec() { doCommandB(); }}; abstract void exec(); } 

这有很多优点:

1)不可能在不执行exec的情况下添加一个枚举。 所以你不会错过一个A.

2)你甚至不必把它添加到任何命令地图,所以没有build立地图的样板代码。 只是抽象的方法和它的实现。 (这也可以说是样板,但不会变短)

3)通过查看if的长列表或计算hashCodes并进行查找,您将保存任何浪费的cpu周期。

编辑:如果你没有枚举,但string作为源,只需使用Command.valueOf(mystr).exec()来调用exec方法。 请注意,您必须在execif上使用public修饰符,才能从另一个包中调用它。

您最好使用命令图。

但是,你是否有一套这样的方法来处理你,最终导致大量的地图被敲击。 那么值得用Enums来做。

你可以使用Enum而不使用开关来实现(如果你在Enum中添加一个方法来parsing“value”的话,你可能不需要这个例子中的getter)。 那么你可以做:

更新:添加静态地图,以避免每次调用迭代。 无耻地从这个答案捏。

 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提供的答案是最好的解决scheme。

我只是提供一些片段,以防您使用Java 8并想使用Lambda!

不带参数的命令:

 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类中的方法,它接受任何Object(在本例中命名为param )作为参数。

如果你有多个重叠的“if”语句,那么这是一个使用规则引擎的模式。 参见例如JBOSS Drools 。

只需使用一个HashMap,如下所述:

如果有可能有一个程序(你所谓的命令),这将是有用的数组。

但你可以编写一个程序来编写你的代码。 如果(值='A')commandA(); 否则如果(……………………等

我不确定在各种命令的行为之间是否有任何重叠,但是您可能还想看看可以通过允许多个命令处理一些input值来提供更多灵活性的责任链模式。

如果它做了很多事情,那么会有很多代码,你真的不能逃避。 只要简单易行,给variables赋予非常有意义的名字,评论也可以帮助…