I'm currently processing user input in client classes, however I feel that by doing this the command object's execute method I would be increasing the capability for reuse as one would only need to instantiate the command object to use its functionality rather than relying on the implementation from the client.
As a result, a command object may look like this:
public class CommandA implements Command {
ReceiverA receiverA;
public CommandA(RecieverA receiverA) {
this.receiverA = receiverA;
}
@Override
public void execute() {
Scanner scanner = new Scanner(System.in);
String x = scanner.nextLine();
receiverA.methodA(x);
}
}
I've found a lack of examples so I was wondering if this was a 'good' practice as in the examples I have seen a command object simply uses a method of a receiver.
Hopefully, the user input straight forward enough that a library for command line parsing, or a simple generated parser using a BNF grammar will remove putting a bunch of user input recognition logic in the client.
The strength of the command pattern is in allowing the client to delay execution of a command or a series of commands, and in allowing the client to pass the commands to an invoker of its choosing.
Take the example of some system that exposes a CLI, and does some data manipulation. Let's say you want to model some user operations: 'save-all' and 'print-sample'. Each of these commands may be missing some required data that the user needs to supply. They would presumably also have some other options with defaults. You would use the command line parsing to separate out the user input supplied through the command line, and then wrap or extract the parsed pieces into the SaveAll
or PrintSample
command instances. The glue logic would only be to use the right command type. Your code would be along the lines of:
String[] userInput = ...
Parsed parsedInput = parser.parse(userInput)
Command command;
switch (parsedInput.getCommandName()) {
case 'save-all':
command = new SaveAll(parsedInput);
break;
...
}
If you use the right tool, you can possibly forgo even this glue code; see JCommander for Java/Scala, as an example.
After you've separated the concern for splitting apart user input, your command classes would be given the parsed input, e.g:
public class SaveAll implements Command {
private String targetName;
private char separator = ','
private DataSet dataset;
private Filesystem filesystem;
public SaveAll(Parsed input) {
targetName = input.getTargetName();
if (input.getSeparator() != null) {
separator = input.getSeparator();
}
}
@Injected
public void setDataSet(...) { ... }
@Injected
public void setFilesystem(...) { ... }
public void execute() {
try (Datafile file = filesystem.openForWrite(targetName)) {
for (Row row : dataset.rows()) {
file.writeRow(row, separator);
} // rows loop
} // auto-resource
} // end execute()
}
Finally, in this example, the client (the tool that exposed the CLI to the user) may allow users to connect to various systems. And underneath, it would have an invoker for each system. The command would therefore be handed over to the appropriate invoker. (And that invoker may well serialize or queue up the command.)