抱歉,我找不到回答这个问题的问题,我几乎可以肯定其他人之前提出过这个问题了。
我的问题是我正在编写一些系统库来运行嵌入式设备。我有命令可以通过无线电广播发送到这些设备。这只能通过文字来完成。在系统库里面,我有一个线程来处理看起来像这样的命令
if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() }
else if etc.
问题在于它有很多命令会迅速失控。看起来很可怕,调试很痛苦,并且在几个月的时间里难以理解。
答案 0 :(得分:167)
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 :(得分: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();
}
当然,您可以将参数传递给doCommand或返回类型。
如果doCommand的实现并不真正“适合”枚举类型,那么这个解决方案可能并不合适,这就像往常一样,当你必须做出权衡时 - 有点模糊。
答案 2 :(得分: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;
}
如果您有多个命令,请查看使用Command模式,如其他地方所述(尽管您可以保留枚举并将调用嵌入枚举中的实现类,而不是使用HashMap)。有关示例,请参阅Andreas或jens对此问题的回答。
答案 3 :(得分:7)
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();
答案 4 :(得分:5)
我建议创建命令对象并使用String作为键将它们放入哈希映射中。
答案 5 :(得分:3)
即使我相信命令模式方法更倾向于最佳实践并且长期可维护,这里有一个单行选项:
org.apache.commons.beanutils.MethodUtils.invokeMethod(这一点, “doCommand” +值,NULL);
答案 6 :(得分: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方法。
请注意,您必须在要从其他包中调用它的execif上使用public修饰符。
答案 7 :(得分:2)
你可能最好使用命令图。
但是你有一套这样的东西来处理你最终的大量地图敲门。那么值得看看用Enums做这件事。
如果向Enum添加方法以解析“value”,则可以使用Enum而不使用开关(在示例中可能不需要getter)。然后你可以这样做:
更新:添加静态地图以避免每次调用迭代。无耻地捏住this answer。
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();
}
}
答案 8 :(得分:2)
在我看来,@ 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
类中的一个方法,它接受任何Object(在此示例中名为param
)作为参数。
答案 9 :(得分:1)
如果您有多个imbricated'if'语句,那么这是使用规则引擎的模式。例如,请参阅JBOSS Drools。
答案 10 :(得分:0)
只需使用HashMap,如下所述:
答案 11 :(得分:0)
如果有可能有一系列程序(你称之为命令)那么有用..
但您可以编写一个程序来编写代码。这一切都非常系统化 if(value ='A')commandA();其他 如果(........................ e.t.c。
答案 12 :(得分:0)
我不确定您的各种命令的行为是否有任何重叠,但您可能还想查看Chain Of Responsibility模式,它可以通过允许多个命令处理某些命令来提供更大的灵活性输入值。
答案 13 :(得分: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"
}
答案 14 :(得分:-1)
如果它做了很多事情,那么会有很多代码,你真的无法摆脱它。只是让它易于理解,给变量非常有意义的名称,评论也可以帮助......