tags:

views:

113

answers:

6

Hello, I have a list of XML tags and a method for each of them inside my class, getting that tag as an argument and doing its work. So all the methods get the same input and I want to loop through the list of tags, calling appropriate method each time.

In Python I've made it with a hash of strings (names of tags) to lambdas (self.methodName()) and I want to translate it to Java.

Now in Java, I can't (?) make such a hash and even can't use strings (tag names) in a switch statement (with each branch calling a certain method). The possibility of using 10 or so subsequent ifs seems horribly ugly and I'm looking for a better way to code that.

+3  A: 

Map string to a class instance by instantiating classes and saving them (probably in a hash). All the classes must implement the same interface of course.

You'll find that if you code this way a better structure starts to emerge from your code--for instance you might find that where before you might have used 2, 3 or 10 similar methods to do slightly different things, now the fact that you can pass data into your constructor allows you to do it all with one or two different classes instead.

This interface and the classes that implement it (for me at least) nearly always evolve into a full-featured set of classes that I needed all along but might not have recognized otherwise.

Somehow I never seem to regret writing code the "Hard" way, but nearly always regret when I choose the easier path.

Bill K
I could pass data to methods before too: `lambda tag: self.method(tag)` and then `hash[tagname](tag)`, now I need like 15 more files...
roddik
Yes, or you could have something readable and extendable. For instance, if you found your "Method" needed data and a second related method, how do you fix it? On top of that, my point was that coding it as classes in the first place often shows you holes in your object model--objects you should have had all along but missed in your initial design.
Bill K
+1  A: 

I'd go with what Bill K suggested in regards to implementing the same interface. But if you have the issue of wanting to call methods with different names you could try using reflection and do something like this:

Method method = Foo.class.getDeclaredMethod("methodName", parametersTypes); // Get the method you want to call
Foo foo = new Foo();
method.invoke(foo, args); // invoke the method retrieved on the object 'foo' with the given arguments
digiarnie
A: 

you can invoke the method using reflection:

Class.getMethod

therefore you don't need a switch or a set of ifs.

pstanton
oh, you can't use reflection? come on downvoter.
pstanton
You could use reflection to implement a "Hello world!" program. If it is a good approach is a different question.
jarnbjo
so reflection is only useful for hello world programs now. ok.
pstanton
@pstanton - given the runtime cost of using reflection to do this, I'd say that this is a bad solution. If you want to avoid the switch, or chain of ifs, there are better ways.
Stephen C
but it depends on the usage (ie maybe once at startup) as to whether a 50ms saving is better than simpler more maintainable code. doesn't matter, just not worth the downvote imo.
pstanton
A: 

An indirect answer: XML typically represents data, not instructions. So it is probably more useful to map parser handling onto fields. This is what JAXB does. I suggest using JAXB or similar.

Unless you have a huge amount to do, I would strongly advise against reflection in a statically typed language. A string of } else if (tag.equals("blah")) { (or with interning, } else if (tag == "blah") { isn't going to kill you. You can even map strings onto their enum namesakes, but that is a little reflectiony. Switch-on-string should be with us in JDK7.

Tom Hawtin - tackline
+1  A: 

What do people think of this?

public static enum Tags {
   TAG1, TAG2, TAG3
}

public class Stuff {
   ...
   switch (Tags.valueOf(str)) {
   case TAG1: handleTag1(); break;
   case TAG2: handleTag2(); break;
   case TAG3: handleTag3(); break;
   }
}

The upside is that this is concise and efficient (at least in this case). The downside is that it is not so good with mixed case tags and tags with Java non-identifier characters in them; e.g. "-". (You either have to abuse accepted Java style conventions for the enum member identifiers, or you have to add an explicit String-to-enum conversion method to the enum declaration.)

Using a switch statement for dispatching is evil in some peoples' book. But in this case, you need to compare what you are gaining with what you are loosing. And I'd be surprised if polymorphic dispatching would give a significant advantage over a switch statement in terms of extensibility and maintainability.

Stephen C
A: 

Here is an example of the proposal of Bill K (if I understood it right)

public class Example {

    static interface TagHandler {
        void handle(String tag);
    }

    static final Map<String, Example.TagHandler> tagHandlers = new HashMap<String, Example.TagHandler>() {
        {
            put("tag_1", new Example.TagHandler() {
                public void handle(String tag) {
                    System.out.println("Handling tag_1: " + tag);
                }
            });

            put("tag_2", new Example.TagHandler() {
                public void handle(String tag) {
                    System.out.println("Handling tag_2: " + tag);
                }
            });
        }
    };

    public static void main(String[] args) {

        String[] tags = { "tag_1", "tag_2", "tag_1" };

        for (String tag : tags) {
            tagHandlers.get(tag).handle(tag);
        }
    }
}
pgras