【问题标题】:Switch statement or remotely invoke methodsswitch 语句或远程调用方法
【发布时间】:2014-01-17 14:03:10
【问题描述】:

我有一个 switch 语句,它将 String 与一组 String 进行比较,其中每个匹配调用不同的方法。

switch(((Operation) expr.getData()).getValue()){
        case "+":
            return add(expr.getNext());
        case "car":
            return car(expr.getNext());
        case "cdr":
            return cdr(expr.getNext());
        case "cons":
            return cons(expr.getNext(), expr.getNext().getNext());
        case "quote":
            return quote(expr.getNext());
        case "define":
            handleDefine(expr.getNext());
            break;
        default:
            return null;
        }

但是,对我来说,这听起来像是可以使用HashMap 更优雅、更有效地实现的东西,该HashMap 链接到包含Method 和参数数量的Operation,因此我可以使用每种方法来HashMap 喜欢:

nameToOperation.put("+", new Operation("+", 1, Driver.class.getMethod("add")));
nameToOperation.put("car", new Operation("car", 1, Driver.class.getMethod("car")));

所以会有N个不同的Operation类实例,每个实例包含String、Method和参数个数

然后我可以简单地使用类似于此的方法调用该方法(我知道这不是您使用调用的方式):

Operation op = ((Operation) expr.getData())

if(op.getNumPars() == 1)
    return(op.getMethod().invoke(expr.getNext()));
else
    return(op.getMethod().invoke(expr.getNext(), expr.getNext().getNext()));

但是,我仍然不完全喜欢这个解决方案,因为我正在失去类型安全性,而且它看起来仍然不是那么好。我在 stackoverflow 上看到的另一个看起来非常优雅但我不完全理解的示例是最佳答案的第一个解决方案:How to call a method stored in a HashMap? (Java)

Stackoverflow 上的每个人都认为最好的解决方案是什么?

编辑:以防万一有人搜索这个并想知道我的解决方案,我让每个操作,如 Add、Car、Cdr 都有自己的类来实现Command。然后我不得不让我的大部分方法都是静态的,我想它们本质上都是静态的。这似乎比原来的 case 语句更优雅。

【问题讨论】:

  • 这似乎是 Action 或 Commannd Pattern 的用例。我在plugin application 中使用了这种模式来启用OSGi 类似的命令来通过控制台加载或卸载插件。命令在初始化到控制台后添加,甚至可以通过进一步动态加载的插件添加
  • 我想说,如果您只在一个地方执行此操作,那么请坚持使用 switch 语句。使用反射或命令模式只会导致更多的代码行,而 imo 不会使代码更易于管理。但是,如果您在多个位置复制粘贴此 switch 语句(或此 switch 语句的一部分),那么情况就不同了,在这种情况下我会使用命令模式。

标签: java reflection methods hashmap switch-statement


【解决方案1】:

基本上,答案建议使用命令模式。

"The main advantage of the command design pattern is that it decouples the object that invokes the operation from the one that know how to perform it. And this advantage must be kept. There are implementations of this design pattern in which the invoker is aware of the concrete commands classes. This is wrong making the implementation more tightly coupled. The invoker should be aware only about the abstract command class"
  1. 基本上,您的地图将是类型安全的。通过声明 Map <character,Command>
  2. 对可扩展性开放

【讨论】:

  • 好的,这个解决方案看起来不错。看来我必须为每个方法创建一个新类,所以有一个实现 Command 的 AddMethod 类,并且 Map 条目将是 map.put("+", new AddMethod());但是,我的 add 方法调用了类中的其他方法,这些方法现在不起作用,因为它在不同的类中,你知道我该如何解决这个问题吗?编辑:switch 语句在一个名为评估的方法中,从那里调用的每个方法都有一个回调来评估。
  • 是的,您需要为每个命令设置单独的类。我还建议考虑@Sam Yonnou 评论,如果您打算在多个地方使用它.. 然后使用命令模式。可以找到简单示例en.wikipedia.org/wiki/Command_pattern
【解决方案2】:

看起来您正在尝试编写一个 Scheme 解释器。在这种情况下,您无论如何都需要一张地图,因为您需要存储所有用户定义的值和函数。 当用户写例如(定义(添加 a b)(+ a b)),您使用“添加”作为键将函数存储在映射中。 但是你的函数应该使用列表作为输入,即每个函数只有一个参数,它是一个列表。顺便说一下,在 Scheme 中,所有表达式都是列表。通常一个 Scheme 解释器由一个 reader 和一个 evaluator 组成。阅读器将代码转换成一堆嵌套列表。 所以基本上“(define (add a b) (+ a b))”可以转换成类似这样的列表结构。

List<Object> list = new ArrayList<Object>();
List<Object> list2 = new ArrayList<Object>();
list2.add("add"); list2.add("a"); list2.add("b");
List<Object> list3 = new ArrayList<Object>();
list3.add("+"); list3.add("a"); list3.add("b");
list.add("define"); list.add(list1); list.add(list2);

当然,您的代码实际上并不像这样,而是通过解析输入代码的递归方法构造列表。

顺便说一句,这些列表不仅包含字符串,还包含数字和布尔值。像这样的嵌套列表是抽象语法树 (AST) 的最简单形式。由于 Scheme 的语法比大多数其他语言的语法要简单得多,一个非常简单的列表结构就足以存储解析后的代码。

然后评估器处理这些列表。 要评估列表,您首先递归评估列表中的每个元素,然后将第一个元素应用于列表的其余部分。因此,第一个元素必须是用户定义的函数或内置命令,例如“定义”。

【讨论】:

  • 正确,我已经进入该项目,但可能太远了,无法更改我的内部数据结构。我目前表示数据的方式是使用类似于点对表示法的东西,其中我有一个层次结构:SExpression + Atom 扩展表达式所以一个 SExpression 有一个数据字段和一个下一个字段,其中数据是一个表达式(Atom 或另一个 SExpression),下一个字段是一个 SExpression。 (我还必须放入一个父字段)。所以当我调用一个程序时,我会发送下一个字段。我通过将用户定义保存在定义对象的 ArrayList 中来处理用户定义
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多