应用程序
我正在编写一个应用程序,它根据用户输入执行某些功能。例如,如果用户输入"12加1",则输出为"3"。我的目标是实现许多这样的方法(div、modulo等(。当我的Scanner识别出像"add"这样的函数名时,应该调用函数"add(("。
我的方式
我的方法是让FunctionHandler类评估输入。
Main:
String inputCommand = sc.nextCommand();
functionHandler.handle(inputCommand);
功能处理程序:
public class FunctionHandler {
public void handle (String functionName) {
if (functionName.equals("add")) {
add();
} else if (functionName.equals("div") {
div();
}
}
private void add() {
.......
}
....
}
这个问题
随着我添加越来越多的函数,if语句变得非常大,当然还有FunctionHandler类。此外,每当我添加一个新函数时,我都必须在两个地方更改代码:我必须定义该函数,然后在handle((中添加else-if子句来调用该函数。这意味着应该封装的两条信息被"存储"起来,彼此完全独立。我想知道解决这种情况的最佳做法是什么?
我的想法
我曾考虑过使用enum,但在这种情况下,它们似乎不太合适。
我的另一个想法是创建一个接口Function,然后为每个实现Function的函数创建一个类。接口有两种方法:
然后,我可以在FunctionHandler中(手动(创建函数的数组,通过它我可以循环查看用户输入的命令是否与getName((匹配。然而,为每个函数指定一个不同的类也不是很干净,而且它也不能解决我添加的每个函数必须在两个地方执行的问题:类和数组。
这个问题只是关于找出如何干净地解决这个问题。如果有一个指向正确方向的指针,我们将不胜感激!
非常感谢!
另一个选项是保留一个处理程序的Map
。如果您使用的是Java8,它们甚至可以是方法引用。
// InputType and ResultType are types you define
Map<String, Function<InputType, ResultType>> operations = new HashMap<>();
operations.put("add", MathClass::add);
// ...
ResultType result = operations.get(userInput).apply(inputObject);
这样做的一个缺点是,所有操作的输入和输出类型都必须相同。
您可以为各种函数创建自定义注释。然后,您可以使用您的数组思想,但让它使用反射来发现哪些函数具有新的注释以及它们的名称。
作为背景,请看http://www.oracle.com/technetwork/articles/hunter-meta-2-098036.html和http://www.oracle.com/technetwork/articles/hunter-meta-3-092019.html.它们有点过时了,但似乎解决了必要的想法。
如果您想要一个简短的解决方案,您可以始终使用反射。
在handle
方法中,您可以执行以下操作:
Method m = this.getClass().getMethod(functionName, new Class[]{});
m.invoke(this, new Object[]{});
假设你没有很多想要这样做的函数,也不想让自己暴露在反射引起的安全风险中,你可以使用字符串开关,比如:
void handleFunction(String function) {
switch (function) {
case "foo":
foo();
break;
case "bar":
bar();
break;
default:
throw new IllegalArgumentException("Unknown function " + function);
break;
}
}
从Java7开始,您可以在switch语句中使用Strings,编译器将从中获得合理的
我会这样做:
public class FunctionTest {
private static final Map<String, Runnable> FUNCTIONS = new HashMap<String, Runnable>() {{
put("add", () -> System.out.println("I'm adding something!"));
put("div", () -> System.out.println("I'm dividing something!"));
}};
public void handle(String functionName) {
if (!FUNCTIONS.containsKey(functionName)) {
throw new IllegalArgumentException("No function with this name: " + functionName);
}
FUNCTIONS.get(functionName).run();
}
}
您基本上可以使用任何函数接口来代替Runnable
,我使用过它,因为它与您的add()
方法相匹配。您可以将函数的名称映射到它们的实际可执行实例,从Map
中按名称获取它们并执行它们。
您还可以使用所需的可执行块创建enum
:
public class FunctionsAsEnumsTest {
private static enum MyFunction {
ADD {
@Override public void execute() {
System.out.println("I'm adding something");
}
},
DIV {
@Override public void execute() {
System.out.println("I'm dividing something");
}
};
public abstract void execute();
}
public void handle(String functionName) {
// #toUpperCase() might not be the best idea,
// you could name your enums as you would the methods.
MyFunction fn = MyFunction.valueOf(functionName.toUpperCase());
fn.execute();
}
}