Java中if语句的长列表

Ste*_*eve 100 java design-patterns command-pattern

对不起,我找不到回答这个问题的问题,我几乎可以肯定其他人已经提出了这个问题.

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

if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() } 
else if etc. 
Run Code Online (Sandbox Code Playgroud)

问题是它有很多命令会迅速失控.看起来很可怕,调试很痛苦,并且在几个月的时间里难以理解.

dfa*_*dfa 169

使用命令模式:

public interface Command {
     void exec();
}

public class CommandA() implements Command {

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

// etc etc
Run Code Online (Sandbox Code Playgroud)

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

commandMap.put("A", new CommandA());
commandMap.put("B", new CommandB());
Run Code Online (Sandbox Code Playgroud)

然后你可以用以下代码替换你的if/else if链:

commandMap.get(value).exec();
Run Code Online (Sandbox Code Playgroud)

编辑

您还可以添加特殊命令,例如UnknownCommandNullCommand,但您需要一个CommandMap处理这些极端情况的命令,以便最大限度地减少客户的检查.

  • 您可以使用Java枚举代替HashMap,它为您提供了一组定义明确的命令,而不是糊状的地图.您可以在枚举中使用getter:命令getCommand(); 或者甚至在枚举中实现exec()作为抽象方法,每个实例都实现(enum as command). (10认同)
  • 好的,对于小型且永不增长的命令集,枚举是一种可行的解决方案. (5认同)
  • 当然,为简单起见,我省略了一些样板代码 (3认同)
  • 这将强制执行枚举中的所有命令……这是非常不理想的。通过一个接口,您还可以应用Decorator模式(例如DebugCommandDecorator,TraceCommandDecorator),在简单的Java界面中内置了更多的灵活性。 (2认同)

jen*_*ens 12

我的建议是enum和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();
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以将参数传递给doCommand或具有返回类型.

如果doCommand的实现并不真正"适合"枚举类型,那么这个解决方案可能不太合适,这就像往常一样,当你必须做出权衡时 - 有点模糊.


Jee*_*Bee 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 对于您添加的每个新命令,您需要编辑开关:此代码不遵循开/关原则 (5认同)

Ble*_*eek 7

dfa简洁明了地演示了一个简洁明了的界面(以及"正式"支持的方式).这就是界面概念的意义所在.

在C#中,我们可以为喜欢在c中使用functon指针的程序员使用委托,但DFA的技术是使用方法.

你也可以有一个数组

Command[] commands =
{
  new CommandA(), new CommandB(), new CommandC(), ...
}
Run Code Online (Sandbox Code Playgroud)

然后你可以通过索引执行命令

commands[7].exec();
Run Code Online (Sandbox Code Playgroud)

从DFA抄袭,但有一个抽象的基类而不是接口.注意稍后将使用的cmdKey.根据经验,我意识到设备命令经常也有子命令.

abstract public class Command()
{
  abstract public byte exec(String subCmd);
  public String cmdKey;
  public String subCmd;
}
Run Code Online (Sandbox Code Playgroud)

这样构造你的命令,

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

  public byte exec()
  {
    sendWhatever(...);
    byte status = receiveWhatever(...);
    return status;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过提供键值对吸引功能来扩展通用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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后构建您的命令库:

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

现在你可以客观地发送控件

commands.get("A").exec();
commands.get(condition).exec();
Run Code Online (Sandbox Code Playgroud)


keu*_*leJ 5

我建议创建命令对象并使用String as Key将它们放入哈希映射中.