Reputation: 4129
I have a particular issue where I have a general class Command
I am using to represent commands in a buffer in sequence. I also have several child classes of it, like ComputeCommand
, DefineCommand
, DisplayCommand
, etc.
The constructor for each subclass takes a String
argument, which the command parses according to the command syntax, which varies from command type to command type.
I want to be able to do something like:
String [] lines = { ... };
ArrayList<Command> commands = new ArrayList<Command>();
for(String line: lines){
commands.add(new Command(line))
}
However, I need the more specialized classes to be able to process each command.
If I could define the constructor something like:
Command(String line){
if(ComputeCommand.isValidComputeCommand(line))
return new ComputeCommand(line);
else if(DisplayCommand.isValidDisplayCommand(line))
return new DisplayCommand(line);
[ ... ]
}
That would accomplish what I need, but constructors don't work that way, as we all know.
One way I have thought of is to include a method like
static Command getInstance(String line){ [...] }
to perform that function, however I am not sure that that is the correct approach in this situation.
Another possible way is to create factory classes for each one:
class CommandFactory(){
CommandFactory [] subFactories = {new ComputeCommandFactory(), [...]};
Command makeCommand(String line){
for(CommandFactory subFactory: subFactories)
if(subFactory.canMake(String line))
return subFactory.makeCommand(line);
}
boolean canMake(String line){
for(CommandFactory subFactory: subFactories)
if(subFactory.canMake(String line))
return true;
}
}
I think this may be the most oop-correct way to do this, but I don't want to add the extra complexity if there is a better way.
What is the correct design pattern to use in this situation?
EDIT:
I also plan to subclass some of the commands further, for instance the ComputeCommand
could have an AddCommand
or a MultiplyCommand
subclass.
Upvotes: 4
Views: 790
Reputation: 38414
As you thought, your CommandFactory
approach is the closest to correct. Factories / factory methods are usually used for this kind of problem.
The only issue you obviously see is that this CommandFactory
has to know about all the subfactories, has to know about all the types. This means that if you add a new type, you'll have to add it to the CommandFactory
, too.
But! You can use e.g. the ClassFinder
from the org.clapper.util
library to find all classes implementing Command
or extending CommandFactory
in your classpath. This way, you can add any new Commands
in the future and the CommandFactory
will still find them and add them to subFactories
.
You can also reverse the problem - let the subfactories (or the individual Commands
themselves) know about the factory! First find them with ClassFinder
, then call a method on them that will add them to the factory list. It's similar approach to how listeners work.
Upvotes: 2
Reputation: 463
You are on the right track. The pattern you are looking to work with here is the Factory Pattern. Your thinking is right, and patterns are nothing but such thinkings that have been "written" down on a paper :).
So, for your case, you can do something like this:
public class CommandFactory
{
public static Command getCommand (String line)
{
Command command = null;
if (ComputeCommand.isValidComputeCommand (line))
{
command = new ComputeCommand (line);
}
else if (DisplayCommand.isValidDisplayCommand (line))
{
command = new DisplayCommand (line);
}
return command;
}
}
and use it like this:
Command command = CommandFactory.getCommand (line);
Hope that helps.
Upvotes: 1
Reputation: 31648
I think you need a combination of Factory
and Chain of Responsibility
patterns to do what you want.
interface CommandFactory{
Command create(String line);
void addToChain(CommandFactory next);
}
If your factory can't handle the given line, then it passes the line to the next factory in the chain:
class ComputeCommandFactory{
CommandFactory next;
Command create(String line){
if(<can process>){
return new ComputeCommand(line);
}
if(next ==null){
throw Exception("can't process");
}
return next.create(line);
}
}
This way you can add new factories later (even at runtime). This is similar to how adding classloaders and Database Drivers work in Java.
Upvotes: 1
Reputation: 3996
What I am reading is that you want to build the correct object based off of context? You have some string, and you want to build the correct object from the contents of that string?
You could have a CommandBuilder/Factory, and that's where you can place your if-else logic to return the right command.
public Command buildCommand(String line){
if(ComputeCommand.isValidComputeCommand(line))
return computeCommandFactory.buildCommand(line);
else if(DisplayCommand.isValidDisplayCommand(line))
return displayCommandFactory.buildCommand(line);
[ ... ]
}
It sounds like you want to use a builder or an abstract factory to build the Command objects you require. When you have a large if-else if chain, you generally want to use a strategy pattern.
Upvotes: 1