If your commands are stateless, thread-safe objects, you can create a map of eagerly-created instances and use these instances repeatedly:
public class CommandFactory {
private final Map<String, Command> availableCommands;
public CommandFactory(Map<String, Command> availableCommands) {
this.availableCommands = availableCommands;
}
public Command getCommand(String s) {
if (availableCommands.contains(s) {
return availableCommands.get(s);
} else {
// handle error state
}
}
}
For this to be any good (and fulfill the "no changes to the class" requirement) however the factory would not be a static class, with instance (rather than static) getCommand()
method. The available commands then can be injected using a DI framework.
If you need to create a new instance of the Command
implementation for each call, then you cannot really avoid reflection without using the terrible if...else if
chain. I wouldn't be too worried about using it in this case though, Class.newInstance()
is quite readable and not terribly inefficient.
I would still stick with that map approach though even for this, to make it flexible and extendable by configuration. The code would be similar just with Map<String, Class>
, return availableCommands.get(s).newInstance()
and some more exception checking.
A third possible approach is creating a separate factory class for each command type, and having a Map<String, SpecificCommandFactory>
, where you'd query for the appropriate factory and then use that factory to get a new instance of that specific command class - but this is a lot of boilerplate code and can be quite unreadable, so is only suitable if you really need to separate the commands available from the factory:
public iterface SpecificCommandFactory {
Command createCommand();
}
public class Comm_AFactory implements SpecificCommandFactory {
public Comm_A createCommand() {
return new Comm_A();
}
}
public class CommandFactory {
private final Map<String, SpecificCommandFactory> availableCommands;
public CommandFactory(Map<String, Command> availableCommands) {
this.availableCommands = availableCommands;
}
public Command getCommand(String s) {
if (availableCommands.contains(s) {
return availableCommands.get(s).createCommand();
} else {
// handle error state
}
}
}