views:

289

answers:

5
+6  Q: 

Refactor java code

Okay guess this question looks a lot like:

http://stackoverflow.com/questions/519422/what-is-the-best-way-to-replace-or-substitute-if-else-if-else-trees-in-programs

consider this question CLOSED!


I would like to refactor code which looks something like this:

String input; // input from client socket.
if (input.equals(x)) {
  doX();
} else if (input.equals(y)) {
  doY();
} else {
  unknown_command();
}

It is code which checks input from socket to perform some action, but I don't like the if else construction because every time a new command is added to the server (code) a new if else has to be added which is ugly. Also when deleting a command the if else has to be modified.

+8  A: 

Collect those commands in a Map<String, Command> where Command is an interface with an execute() method.

Map<String, Command> commands = new HashMap<String, Command>();
// Fill it with concrete Command implementations with `x`, `y` and so on as keys.

// Then do:
Command command = commands.get(input);
if (command != null) {
    command.execute();
} else {
    // unknown command.
}

To get a step further, you could consider to fill the map dynamically by scanning for classes implementing a specific interface (Command in this case) or a specific annotation in the classpath. Google Reflections may help lot in this.

Update (from the comments) You can also consider combining the answer of Instantsoup with my answer. During the buildExecutor() method, first get the command from a Map and if the command doesn't exist in Map, then try to load the associated class and put it in the Map. Sort of lazy loading. This is more efficient than scanning the entire classpath as in my answer and creating it everytime as in Instantsoup's answer.

BalusC
But then I have to modify the map every time I add/delete a new command? can't I do this dynamically or something?
Alfred
Yes, I realized that afterwards as well and edited it in right before I saw your comment :)
BalusC
@Alfred. You could try reflection, but its hardly the best choice. Go with the map, keep it in a safe place where its easy to add new commands.
Tom
you could inject the implementations into the map with something like Spring, but you won't get around the fact that you **have** to touch some code to add new branches of logic, whether it is an if() block or a map.add(new Command()); or in a Spring XML file.
fuzzy lollipop
Or you could use my answer and Java's SPI mechanism.
Romain
I think your implementation is pretty good, but I am not convinced this is the best solution possible.
Alfred
Why exactly not?
BalusC
I don't know why :). I hope somebody has even better solution. Else I am going to mark your answer
Alfred
You can consider combining the answer of instantsoup with my answer. During the `buildExecutor()` method, first get the command from a `Map` and if the command doesn't exist in map, then try to load the associated class and put it in the map. Sort of lazy loading. This is more efficient than scanning the entire classpath as in my answer and creating it everytime as in instantsoup's answer.
BalusC
+3  A: 

One way could be to have an interface ICommand that is the general contract for a command, e.g.:

public interface ICommand {
    /** @param context The command's execution context */
    public void execute(final Object context);
    public String getKeyword();
}

And then you could use Java's SPI mechanism to auto-discover your various implementations and register them in a Map<String,ICommand> and then do knownCommandsMap.get(input).execute(ctx) or something alike.

This practically enables you to decouple your service from command implementations, effectively making those pluggable.

Registering an implementation class with the SPI is done by adding a file named as the fully qualified name of your ICommand class (so if it's in package dummy the file is going to be META-INF/dummy.ICommand within your classpath), and then you'll load and register them as:

final ServiceLoader<ICommand> spi = ServiceLoader.load(ICommand.class);
for(final ICommand commandImpl : spi)
    knownCommandsMap.put(commandImpl.getKeyword(), commandImpl);
Romain
this is only available in JDK 6
fuzzy lollipop
Could you explain the SPI part a little better?
Alfred
@fuzzy lollipop: Actually it was already in JDK 5, but in a non-public package (sun.misc...). It should've been part of JDK 5 at first.
Romain
@Alfred: Added example of SPI use, assuming you have an initialized map somewhere and stuff ... (I just added the relevant code for the SPI thingie)
Romain
javadocs say "since 1.6" anything in sun.* should not be used
fuzzy lollipop
It'd be nice to mix this with an annotation. You'd use an annotation on the class that provides the command implementation to state what command that implementation is for, and then use an annotation processor to build the list of what command there are during the build phase. (I'm not an expert on annotation processing though; never build a system in the way I describe, but do know that webservice containers do things of this type.)
Donal Fellows
@donal: indeed :)@fuzzy lollipop: I'd agree there. It was in 1.5's draft specification, though.
Romain
+3  A: 

How about interfaces, a factory, and a little reflection? You will still need to handle exceptions on bad input, but you would always need to do this. With this method, you just add a new implementation of Executor for a new input.

public class ExecutorFactory
{
    public static Executor buildExecutor(String input) throws Exception
    {
        Class<Executor> forName = (Class<Executor>) Class.forName(input);
        return (Executor) executorClass.newInstance();
    }
}

public interface Executor
{
    public void execute();
}


public class InputA implements Executor
{
    public void execute()
    {
        // do A stuff
    }
}

public class InputB implements Executor
{
    public void execute()
    {
        // do B stuff
    }
}

Your code example then becomes

String input;
ExecutorFactory.buildExecutor(input).execute();
Instantsoup
That's also a nice idea, it has only the cost of creating a new instance everytime.
BalusC
Yup I also like this idea.
Alfred
Where does the name of the executor come from? If it's client/user input, a malicious user could instantiate any class on the system. If the class initialization changes state on the system, then the attacker would gain some level of control over the system. Then there'd be a race condition between the instantiation and the exception being thrown (assuming the class isn't a subclass of Executor), during which the attacker could take advantage of any functionality opened up by the other class...
atk
@atk True. I'm assuming there is some error handling and input checking involved that is not represented. I'll add a check that the command at least is an Executor.
Instantsoup
+2  A: 

Building the Command patten upon an enum class can reduce some of the boilerplate code. Let's assume that x in input.equals(x) is "XX" and y in input.equals(y) is "YY"

enum Commands {
   XX {
     public void execute() { doX(); }        
   },
   YY {
     public void execute() { doY(); }        
   };

   public abstract void execute();
}

String input = ...; // Get it from somewhere

try {
  Commands.valueOf(input).execute();
}
catch(IllegalArgumentException e) {
   unknown_command();
}
Itay
Also a nice idea, but this is pretty tight coupled. You can't provide commands from "outside" anymore.
BalusC
+1  A: 

You say that you're processing input from a socket. How much input? How complex is it? How structured is it?

Depending on the answers to those questions, you might be better off writing a grammar, and letting a parser generator (eg, ANTLR) generate the input-processing code.

Anon
Just a simple protocol. For example memcached.
Alfred
@Alfred - in the case of memcached, which is a very simple protocol that has a half-dozen operations and is unlikely to change, I'd use an if-else construct. There's no reason to make your code mode complex just to "object-orientify" it. I would, however, create an object to wrap the command and/or response, and handle all the parsing. And probably create an enum to represent the command (which would turn the if-else chain into a switch).
Anon
If I wanted to be able to substitute behavior easily, I could use the Template Method pattern, create abstract methods for each of the operations, and have my app reflectively instantiate subclasses.
Anon