Wolfmans Howlings

A programmers Blog about Programming solutions and a few other issues

Using java enum for command dispatching

Posted by Jim Morris on Sun Apr 12 01:26:06 -0700 2009

I haven't blogged much about Java even though it is my primary programming language. Since I have been doing a lot of Java recently I thought I'd post something about some of the Java idioms I've used over the years.

One I have been using a lot recently is a command dispatcher where the command is text.

In some languages you would probably use a switch statement with the command string being the case statement. However Java does not allow strings as the match part of a case.

A common idiom seen is to use enums (in Java 5 and greater), as they are constants and can be used in a switch statement.

Now enums are very powerful constructs in Java, they are not simply convenient #define work-arounds, although they are commonly used that way...

See this for all the info on enums.

However you can do so much more with enums...

I use them as a super duper switch statement to dispatch text commands, and the cool thing is they are pretty efficient as they use a hash table lookup to do the dispatch. Here's an example of a command line app that uses the java Gnu GetOpt package with long option names that dispatches different commands that can be typed on the command line...

> doit --froboz one two three
> doit --bazznot glozny frobod

where --froboz is a command that takes three parameters, you get the idea.

Ok that looks like a lot of code for a command lookup/dispatcher, but the nice thing about it is it is fairly DRY, to add a new command you simply add another enum term, the rest is taken care of automatically.

The command is looked up in the switches default clause using the valueOf method of an enum which basically finds the enum that matches the given string.

It can be run thusly...

> java -cp ./java-getopt.jar:./classes com.e4net.Test --bazznot 1 2
In bazznot with: 1, 2

> java -cp ./java-getopt.jar:./classes com.e4net.Test --froboz 1 2 3
In froboz with: 1, 2, 3


> java -cp ./java-getopt.jar:./classes com.e4net.Test --blahblah 1 2 3
myprog: unrecognized option '--blahblah'
Usage: myapp [-x filename] [--command] [args...]
    where commands is one of:
    froboz param1 param2 param3
    bazznot param1 param2

A variation on this theme is if say you have an Internet server, that receives text commands over a socket, we want to lookup the command we received and dispatch the same way as we do above. This turns out to be easy, we use the valueOf method of an enum. Below we call handle() with the command we got over the wire, and it is neatly dispatched. valueOf() is the command that will do the lookup in a hash table, so is quite efficient if the command list is long.

The code for this article can be downloaded from here

Posted in Java  |  Tags java,enum,dispatcher  |  2 comments

Comments

  1. Tipster said on Wed Apr 11 09:31:31 -0700 2012
    If you don't like coding your doit() methods inline within the enum, or want to use the same method for multiple commands, then you can declare an abstract class wrapping an abstract method, extend it to create specific classes with specific method implementations, then pass an instance of a wrapper class in your enum constructor. Something like this:

    private static abstract class doitMethodWrapper {
      abstract boolean doit(int off, String [] argv);
    }

    private static class frozbozMethodWrapper extends doitMethodWrapper {
      boolean doit(int off, String [] argv){
        // do whatever froboz does with three arguments
        System.out.println("In froboz with: " + argv[off] + ", " + argv[off+1] + ", " + argv[off+2]);
        return true;
      }
    }

    private static final frozbozMethodWrapper frozbozMethodWrapperInst = new frozbozMethodWrapper();

    private enum Commands {
      frozboz('f', 3, frozbozMethodWrapperInst),
      bazznot('b', 2, frozbozMethodWrapperInst);

      private final int code, size;
      private doitMethodWrapper methodWrapperInst;

      Commands(int code, int size, doitMethodWrapper methodWrapperInst){
        this.code= code;
        this.size= size;
        this.methodWrapperInst = methodWrapperInst;
      }

      public int code() { return code; }
      public int size() { return size; }
      public doitMethodWrapper methodWrapperInst() { return methodWrapperInst; }
    };

    // then instead of invoking by
    // cm.doit(g.getOptind(), args);
    // use
    // cm.methodWrapperInst().doit(g.getOptind(), args);
  2. wolfman said on Wed Apr 11 11:24:18 -0700 2012
    tx tipster thats a good idea

(leave email »)