Java中if语句的详细列表


101

抱歉,找不到答案,我几乎可以肯定有人提出过这个问题。

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

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

问题在于,要执行的命令很多,很快就会变成无法控制的事情。几个月后,看起来很恐怖,调试起来很痛苦,难以理解。


16
只是一句话-我强烈建议您阅读《四大帮派模式》一书,或者,如果您是模式新手,请参阅《 Java中的Head First Design Patterns》一书(这是一本非常容易阅读的书,并且很好地介绍了许多常见模式)。两者都是宝贵的资源,而且都为我的腊肉节省了不止一次。
aperkins 2009年

2
是的,实际上我拥有它们,但是它们不见了:)那就是为什么我确定自己做错了的原因:)虽然找不到正确的解决方案!也许这会在Google上获得一个不错的位置
Steve

2
这只是周一的命令模式!
尼克·维斯

Answers:


171

使用命令模式

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处理这些特殊情况的命令,以最大程度地减少客户的检查。


1
...通过适当的检查,确保commandMap.get()不返回null :-)
Brian Agnew

3
当然,为了简单起见,我省略了一些样板代码
dfa

10
可以使用Java枚举代替HashMap,它可以为您提供一组定义明确的命令,而不是糊状的地图。您可以在枚举中使用getter:Command getCommand(); 甚至将exec()作为枚举中的抽象方法实现,每个实例都将其实现(作为命令枚举)。
JeeBee

2
这将强制执行枚举中的所有命令……这是非常不理想的。通过界面,您还可以应用Decorator模式(例如DebugCommandDecorator,TraceCommandDecorator),简单的Java界面内置了更多的灵活性
dfa

5
好的,对于小的且永不增长的命令集,枚举是一个可行的解决方案。
dfa

12

我的建议是枚举和Command对象的一种轻量级组合。这是约书亚·布洛赫(Joshua Bloch)在“有效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或具有返回类型。

如果doCommand的实现并不真正“适合”枚举类型,则此解决方案可能并不十分合适,这与通常需要进行权衡时一样,有点模糊。


7

有一个命令枚举:

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

如果您有多个命令,请参考其他地方的回答使用命令模式(尽管您可以保留枚举并将对实现类的调用嵌入到枚举中,而不是使用HashMap)。有关示例,请参阅Andreas或jens对这个问题的回答。


5
对于您添加的每个新命令,您需要编辑开关:此代码不遵循打开/关闭原则
dfa

取决于命令是很少还是很多,不是吗?另外,这些天来,这个网站是如此缓慢,需要5次尝试才能编辑答案。
JeeBee

这不是最佳选择,请参见stackoverflow.com/questions/1199646/…,了解如何实现最佳选择。
Andreas Petersson,2009年

是的,谢谢您花费时间来实现我在评论底部编写的内容-Java Enum as Command Pattern。如果我可以编辑我的文章,我会提到这一点,但是这个网站快要死了。
JeeBee

我认为这个问题是在向Switch声明大喊大叫!
迈克尔·布朗

7

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

这样构造您的命令,

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

+1代表的提法,以防万一.NET人员看到此问题并为单一方法界面所困扰。但是它们确实不能与函数指针相提并论。它们更接近命令模式的语言支持版本。
Daniel Earwicker

5

好吧,我建议创建命令对象,并使用String作为键将它们放入哈希图中。


3

即使我相信命令模式方法更倾向于最佳实践并且可以长期维护,这还是一个可行的选择:

org.apache.commons.beanutils.MethodUtils.invokeMethod(this,“ doCommand” + value,null);


2

我通常尝试以这种方式解决它:

public enum Command {

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

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

abstract void exec();
 }

这有很多优点:

1)如果不实现exec就无法添加枚举。这样您就不会错过A。

2)您甚至不必将其添加到任何命令映射中,因此无需用于构建映射的样板代码。只是抽象方法及其实现。(可以说它也是样板,但不会再短了。。)

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

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


2

使用Command Map可能是最好的选择。

但是,您是否拥有一组这样的工具来处理您,最终却遭受大量Maps的袭击。然后值得考虑使用Enums进行操作。

如果在枚举中添加方法来解析“值”,则可以使用枚举来实现,而无需使用开关(示例中可能不需要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();
    }
}

2

我认为@dfa提供的答案是最好的解决方案。

我只是提供一些代码片段,以防您使用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);

在上面的示例中,doSomethingXmyObj类中存在的方法,该方法将任何Object(param在此示例中命名)作为参数。




0

如果可能有一系列有用的过程(您称为命令)。

但是您可以编写一个程序来编写代码。if(value ='A')commandA();都是非常系统的 否则如果(...........................等


0

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


0

命令模式是必经之路。这是一个使用Java 8的示例:

1.定义接口:

public interface ExtensionHandler {
  boolean isMatched(String fileName);
  String handle(String fileName);
}

2.使用每个扩展来实现接口:

public class PdfHandler implements ExtensionHandler {
  @Override
  public boolean isMatched(String fileName) {
    return fileName.endsWith(".pdf");
  }

  @Override
  public String handle(String fileName) {
    return "application/pdf";
  }
}

public class TxtHandler implements ExtensionHandler {
  @Override public boolean isMatched(String fileName) {
    return fileName.endsWith(".txt");
  }

  @Override public String handle(String fileName) {
    return "txt/plain";
  }
}

等等 .....

3.定义客户端:

public class MimeTypeGetter {
  private List<ExtensionHandler> extensionHandlers;
  private ExtensionHandler plainTextHandler;

  public MimeTypeGetter() {
    extensionHandlers = new ArrayList<>();

    extensionHandlers.add(new PdfHandler());
    extensionHandlers.add(new DocHandler());
    extensionHandlers.add(new XlsHandler());

    // and so on

    plainTextHandler = new PlainTextHandler();
    extensionHandlers.add(plainTextHandler);
  }

  public String getMimeType(String fileExtension) {
    return extensionHandlers.stream()
      .filter(handler -> handler.isMatched(fileExtension))
      .findFirst()
      .orElse(plainTextHandler)
      .handle(fileExtension);
  }
}

4.这是示例结果:

  public static void main(String[] args) {
    MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();

    System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
    System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
    System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-powerpoint"
  }

-1

如果它做很多事情,那么将有很多代码,您真的无法摆脱它。只需使其易于理解,为变量赋予非常有意义的名称,注释也可能会有所帮助...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.