简体   繁体   中英

Switch statement or remotely invoke methods

I have a switch statement that compares a String with set of String where each match calls a different method.

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;
        }

However, to me this sounds like something that could be achieved far more elegantly and efficiently using a HashMap that links up to an Operation that contains a Method and the number of parameters so I could each method to a HashMap like:

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

So there would be N different instances of the Operation class each containing the String, Method and number of parameters

And then I could simply call the method using something similar to this (I understand this isn't how you use invoke):

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()));

However, I still don't fully like this solution as I am losing type safety and it still doesn't look that great. Another example I have seen on stackoverflow that looked quite elegant but I don't fully understand is the first solution of the top answer on: How to call a method stored in a HashMap? (Java)

What does everyone on Stackoverflow think the best solution is?

Edit: Just in case anybody searches this and was wondering about my solution, I made each operation such as Add, Car, Cdr have their own class that implemented Command . I then had to make the majority of my methods static, which I suppose by nature each of them were anyway. This seems way more elegant than the original case statement.

basicaly , the answer recommends to go with Command pattern.

"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. Basicaly your map would be type safety. by declaring Map <character,Command>
  2. Open to Extendibility

It looks like you are trying to write a Scheme interpreter. In that case you're gonna need a map anyway since you need to store all the user defined values und functions. When the user writes eg (define (add ab) (+ ab)), you store the function in the map using "add" as key. But your functions should use lists as inputs, ie each function has exactly one argument which is a list. In Scheme all expressions are lists by the way. Usually a Scheme interpreter consists of a reader and an evaluator. The reader converts the code into a bunch of nested lists. So basically "(define (add ab) (+ ab))" could be converted into a list structure similar to this.

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);

Of course your code doesn't actually look like this, instead the lists are constructed by recursive methods parsing the input code.

Those lists don't just contain strings btw., they also contain numbers and boolean values. Nested lists like this are the most simple form of an abstract syntax tree (AST). Since the syntax of Scheme is much simpler than that of most other languages, a very simple list structure is enough to store the parsed code.

The evaluator then processes those lists. To evaluate a list you first recursively evaluate every element in the list and then apply the first element to the rest of the list. That first element must therefore be a user defined function or a build in command eg "define".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM