Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactor java code

Okay guess this question looks a lot like:

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.

like image 556
Alfred Avatar asked Mar 31 '10 20:03

Alfred


2 Answers

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.

like image 167
BalusC Avatar answered Oct 27 '22 14:10

BalusC


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);
like image 42
Romain Avatar answered Oct 27 '22 12:10

Romain