Command pattern vs reflection

1.7k views Asked by At

I have controller that executes some commands according to command name, taken from url. The main point is in not to use if and switch clauses. As I know there are ONLY two ways how to do it - 1) command pattern 2) reflection.

//Command pattern
class Controller{
  private HashMap<String,Command> commands;
  public void executeCommand(String commandName){
    commands.get(commandName).execute();
  }
  ...
}

//reflection
class Controller{
  public void readCommand(){
    ....
  }
  public void executeCommand(String commandName){
    this.getClass().getMethod(commandName+"Command").invoke(this);
  }
  ...
}

So the questios:

  1. Which one is better?
  2. Is it normal in one application to let developers use one of the methods they want.
  3. Are there other ways?
4

There are 4 answers

0
Kamil.H On BEST ANSWER
  1. first way is better, use reflections only when don't have other options.
  2. in one application there should be one approach to solve one kind of problem.
  3. I think the first approach is fine. (much better then if/else blocks)
1
Kenyakorn Ketsombut On

I think there are 2 different ways for your first approach. Each command could be a subclass of the abstract class Command. Or each command could be an instance of the class command. That depends on how flexible that is all supposed to be, and are the are parameters and return values for commands? With subclasses it would look like this (just to get the idea):

abstract public class Command {
    abstract public void execute();
}

public class LsCommand extends Command
{
    @Override
    public void execute() {
        try {
            Runtime.getRuntime().exec("ls");
        } catch (IOException e) {}
    }
}

public class ChdirCommand extends Command
{
    @Override
    public void execute() {
        try {
            Runtime.getRuntime().exec("chdir");
        } catch (IOException e) {}
    }
}

Here my answers:

  1. Your first way is better. Always prefer design patterns over reflection.
  2. Sorry I don't understand question 2. But it doesn't have a question mark anyway, so I just skip it :)
  3. You might want to look into the Strategy design pattern, where each command could even be made of different parts of sub commands. Another idea would be the Factory design pattern. In that case you would put each command into a class and then use the ClassLoader to load the class by name.
0
Ravindra babu On

Which one is better?

Obviously first one is better. Even though you have quoted that you are using Command pattern, it's not complete "Command" pattern. Command pattern will have Command (abstract), Concrete Command, Receiver, Invoker and Client.

Have a look at this question:

Using Command Design pattern

Apart from Command Patten, I would like to highlight pros and cons of reflection.

Pros:

  1. Handling dependency injection
  2. Developing plug and play frameworks

Cons:

  1. Reflection calls are slower
  2. You can violate security and explode application with bad intent ( e.g. setting private variables of a class, which is invisible to other class)

Have a look at related SE question regarding Reflection :

What is reflection and why is it useful?

Is it normal in one application to let developers use one of the methods they want.

It is normal for developers to chose best method to solve a particular problem.

Are there other ways?

It depends on type of problem you are going to address. Design patterns provides solutions to recurring problems.

All solution can't be fit in existing design patterns. You may have developed new patterns to solve your problem.

0
Yuriy N. On

It is not obviously one vs another. You can have both:

public class Controller {
     public void executeCommand(String commandName){
          CommandFactory.getCommand(commandName).execute();
      }         
} 
public class CommandFactory {

private static final String COMMAND_PACKAGE = "come.example.command.";

private static Map<String, Command> commandMap = new HashMap<>();

public static Command getCommand (String commandName){
    if (commandMap.containsKey(commandName)){
        return commandMap.get(commandName);
    }
    String commandNameCapitalized = commandName.substring(0, 1).toUpperCase() + commandName.substring(1);
    String commandClassString = COMMAND_PACKAGE + commandNameCapitalized;
    try {
        Class commandClass = Class.forName(commandClassString);
        Command command = (Command) commandClass.newInstance();
        commandMap.put(commandName, command);
        return command;
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
   }

}