字符串输入,指定要调用的函数[Java][Best Practice]



应用程序

我正在编写一个应用程序,它根据用户输入执行某些功能。例如,如果用户输入"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的函数创建一个类。接口有两种方法:

  • getName((
  • execute((
  • 然后,我可以在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();
        }
    }
    

    相关内容

    • 没有找到相关文章

    最新更新