Best Java design pattern to handle console commands - java

I'm trying to create a console to handle console commands from a string.
At the moment I'm using if statements to check for each command like this:
if (command.contains("new train")) {
command = command.replace("new train ", "");
Train t = new Train();
t.setCode(command);
ServiceProvider.getTrainService().saveOrUpdate(t);
responeHandler("train " + command + " created");
}
But this isn't the best solution in my opinion.
I'm wondering whether there is already a good design pattern for problems like this?
I have looked at the builder and factory patterns but can't really decide if they are the right choice.

A Command and Factory pattern maybe?
interface Command {
void execute();
}
interface CommandFactory {
boolean canCreate(String input);
Command fromInput(String input); // or return Optional so it can be a FunctionalInterface
}
class TrainCommand implements Command {
String train;
public TrainCommand(String t) { train = t; }
public void execute() {
ServiceProvider.getTrainService().saveOrUpdate(t);
}
}
class TrainCommandFactory {
public boolean canCreate(String t) {
return t.contains("new train ");
}
public Command fromString(String c) {
return new TrainCommand(c.replace("new train ", ""));
}
}
And a Singleton Composite CommandFactory that iterates all known Command Factories:
class CommandFactories implements CommandFactory {
private static final CommandFactories INSTANCE;
private List<CommandFactory> delegates = Arrays.asList(
new TrainCommandFactory()
// others
};
public boolean canCreate(String t) {
return delegates.stream()
.filter(cf -> cf.canCreate(t))
.findAny().isPresent();
}
public Command fromString(String c) {
return delegates.stream()
.filter(cf -> cf.canCreate(t))
.findAny()
.map(CommandFactory::fromString);
}
}

You could store your commands in an array and when the user enters a command, you could find the item having the given index. The index would be meaningful and usable in a switch-case and if the item is not found, you could give a meaningful response.
Also, you could do this in a case-insensitive manner of having the keys in the array all lower characters and turning the command key to lower before the search:
protected String[] supportedCommands = {"first", "second", "third"};
public static int findCommand(String command) {
for (var i = 0; i < supportedCommands.length; i++) {
if (command.equals(supportedCommands[i])) return i;
}
return -1; //Not found
}
public static void handleCommand(String command) {
int c = findCommand(command.toLowerCase());
switch (c) {
case 1: {/*...*/} break;
default: {/*Handle Undefined command*/}
}
}

I think using defined commands is the proper way. An important issue for a command would be to be identified (matches) by the commandString and to be executed (execute). Once you have created custom Command you can register them in a List and execute them.
interface Command{
boolean matches(String commandString);
boolean execute(String commandString);
}
an Example implementation would be
CreateTrainCommand implements Command{
private final CMDSTRING = "new train";
#Override
public boolean matches(CommandString cmdStr){
if(cmdStr != null && cmdStr.toLowerCase.startsWith(CMDSTRING)){
return true;
}
return false;
}
#Override
public boolean matches(CommandString cmdStr){
if(cmdStr != null){
String train = cmdString.toLowerCase.replace(CMDSTRING, "").trim();
//here comes your command execution
Train t = new Train();
...
}
return true;//execution succesful
}
}
if you want to use these command store all commands into a List (or any other collection) and check if your command matches your input
List<Command> cmds = ...
cmds.add(new CreateTrainCommand()); //add all commands that were implemented
//or only some commands if the user has restricted access
here is how you apply the commands
String commandString = ... //from Scanner or where else
for(Command cmd: cmds){ //use streams if you're java 8
if (cmd.matches(commandString)){
boolean wasSuccesful = cmd.execute(commandString);
break;
}
}

A Map<String, Consumer<String>> could do the job to associate commands to actions.
It is not the GOF factory and command DP.
But these are fair and simple implementations of factory and command pattern.
So you should consider it too.
Map<String, Consumer<String>> actionsByCommand = new HashMap<>();
actionsByCommand.put("new train", command->{
command = command.replace("new train ", "");
Train t = new Train();
t.setCode(command);
ServiceProvider.getTrainService().saveOrUpdate(t);
responeHandler("train " + command + " created");
});
actionsByCommand.put("delete train", command->{
command = command.replace("delete train ", "");
...
});
// and so for...
You could also create a special action for invalid commands that you don't need to put in the map :
Consumer<String> invalidCommandAction = (command-> System.out.println("Invalid command. Here are the accepted commands..."));
To improve the testability and the maintainability of action classes, you could
move them into distinct classes.
Map<String, Consumer<String>> actionsByCommand = new HashMap<>();
actionsByCommand.put("new train", new NewTrainCommand());
actionsByCommand.put("delete train", new DeleteTrainCommand());
With NewTrainAction defined as :
public class NewTrainAction implements Consumer<String>{
public void accept(String command){
command = command.replace("new train ", "");
Train t = new Train();
t.setCode(command);
ServiceProvider.getTrainService().saveOrUpdate(t);
responeHandler("train " + command + " created");
}
}
And other Actions defined in the same way.
Then you can use them in this way :
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String command = scanner.nextLine();
Consumer<String> action = actionsByCommand.getOrDefault(command, invalidCommandAction);
action.accept(command);
}

If you are in the Spring world you can use
You could consider to implement
org.springframework.boot.CommandLineRunner
Each command could be executed in its own CommandLineRunne instance.
Use
org.springframework.core.env.SimpleCommandLinePropertySource
to parse your command line

Related

How to make an auto-adding command with interface?

I'm coding something like cmd in java, so I made a few commands like (hello world), , etc. All the commands are classes that implement the interface (I named it as "API"), but to identify which command is written, I use the "if" expression, so to add the new command (class), I have to write this expression every time, for instance:
if (command.equals("print")){return new Print();}
Well, that's the question, how can I write an expression once to make java identify a new command (class), no matter how much I add?
Thanks in advance;
That's the code:
This interface helps the program identify which command is written:
public interface UI {
API get();
Function<String, UI> check = command -> {
if (command.equals("print")){return new Print();}
if (command.equals("change")){return new Change();}
if (command.equals("exit")){return new Exit();}
return new NotFound();
};
static UI of(String command){
return check.apply(command);
}
}
An example of a class:
public class Print extends Backend implements API, UI{
#Override
public boolean command() {
System.out.println(string);
return true;
}
#Override
public API get() {
return new Print();
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
boolean run = true;
while (run) {
System.out.print("Java> ");
//UI
UI command = UI.of(scanner.next());
API com = command.get();
run = com.command();
}
}
}

Java 8 Map<String, Runnable> Control Flow

I'm trying to familiarize myself with some new Java 8 functionality (ha) but I'm having some control flow mishaps.
In the following code, I have a Map<String, Runnable> so I can call methods based on their name, but I can't seem to figure out how to do two things:
How can I make these methods take parameters? I.e. what is the syntax I need in the map "put"s.
When I call these methods from the "get" method in "Dispatch, I cannot return the return value of the method (Status). I suspect this has something to do with where the method is being called, but I can't figure it out. Status is just an enum and the toList method just takes a space separated string and returns a list (this is meant to be used as a REPL).
import java.util.*;
public class Dispatcher {
private Map<String, Runnable> func;
private Status status;
private List<String> command;
Optional<List<String>> opt;
public Dispatcher() {
func = new HashMap<>();
func.put("Method1", this::Method1);
func.put("Method2", this::Method2);
func.put("Help", this::Help);
status = Status.DONE;
}
private Status Help() {
return Status.DONE;
}
private Status Method1() {
return Status.DONE;
}
private Status Method2() {
return Status.DONE;
}
/**
* Execute the given command on a new process.
* #param command the full command requested by the caller including command name and arguments.
* #return The status of the requested operation.
*/
public Status Dispatch(String command) {
opt = CommandInterpreter.toList(command);
opt.orElse(new LinkedList<String>(){{add("Help");}});
func.get(opt.get().get(0));
return Status.DONE;
}
}
Here is a skeleton how you may start do deal with commands taking zero or more arguments and returning a status code. It is just a blueprint, an example. Perhaps it helps you getting started:
public class Dispatcher {
public static final int SUCCESS = 0;
public static final int FAILURE = 1;
public static final Command HELP = (args) -> {
String command = args[0];
System.out.println("Usage of " + command + ": bla bla");
return FAILURE;
};
public static final Command RENAME = (args) -> {
File oldName = new File(args[1]);
File newName = new File(args[2]);
return oldName.renameTo(newName) ? SUCCESS : FAILURE;
};
public final Map<String, Command> commands = new HashMap<String, Command>() {{
put("help", HELP);
put("rename", RENAME);
}};
public int dispatch(String commandLine) {
String[] args = commandLine.split("\\s");
return Optional.ofNullable(commands.get(args[0]))
.orElse(HELP)
.execute(args);
}
}
interface Command {
int execute(String... args);
}
If you want the methods to take arguments, then you don't want to store it as Runnable. You might want Consumer, or another custom functional interface that accepts an argument -- if you want a return value, use Function, or create your own interface.
The Runnable interface doesn't accept any parameters or have a return type. To add a return type, you can use Supplier<Status>. To add a parameter, use Function<ParamType, Status>.

Android - Adding functions dynamically in a hash map from Application and Jar

Hi I have wrapper application and a jar which runs in a service of wrapper app. In the complete solution the Jar is my product which I can't revile to users, but I need to give them the liberty to extend the functionality by registering function against a command received from socket. They can do it in the wrapper application. I already have some commands coming from flex UI and they are processed like following:
private void processCommand(String tempCommand) throws NumberFormatException, IOException, ELearningException
{
ApplicationLog.log("Command Queue " + tempCommand, true);
String[] commandParameters = tempCommand.split("#");
switch (Integer.parseInt(commandParameters[0]))
{
case CONSTANTS.INITIALIZE:
if (this.m_isInitialized)
break;
else
{
InitializeTeacherJar.instantiate(tempCommand.split("#")[1], this.baseContext, tempCommand.split("#")[2]);
parent = InitializeTeacherJar.getInstance();
parent.setMyFlexServer(this.m_localServerSocket);
parent.setMyFlexSocket(this.m_localSocket);
this.m_isInitialized = true;
}
break;
case CONSTANTS.LOGIN:
/**
* Store student details in hash map
*/
this.writeToStudentJava(tempCommand, JavaServerThreadForTeacher.getIpAddress().getHostAddress());
if(tempCommand.split("#")[1].equalsIgnoreCase(CONSTANTS.SUCCESS))
{
HashMap<String, ArrayList<String>> temp = parent.getStudentIPList();
ArrayList<String> value= new ArrayList<String>();
value.add(tempCommand.split("#")[3]);
value.add("present");
temp.put(tempCommand.split("#")[2], value);
parent.setStudentIPList(temp);
if (StudentUtility.studentCounter < 0)
StudentUtility.studentCounter = 0;
StudentUtility.studentCounter = StudentUtility.studentCounter + 1;
parent.getMyFlexSocket().getOutputStream().write((CONSTANTS.PING + parent.getDelimiter() + StudentUtility.studentCounter).getBytes());
System.out.print("StudentUtility.studentCounter :: "+StudentUtility.studentCounter);
}
break;
case CONSTANTS.COURSE:
parent.setCourse(tempCommand.split(parent.getDelimiter())[1]);
break;
case CONSTANTS.ACTION:
parent.performAction(tempCommand, commandParameters[3]);
parent.getMyFlexSocket().getOutputStream().write((CONSTANTS.PING + parent.getDelimiter() + StudentUtility.studentCounter).getBytes());
break;
case CONSTANTS.INTERACTIVE:
if (commandParameters[1].equalsIgnoreCase(CONSTANTS.Play)) {
parent.playAudio(commandParameters[2], true);
} else if (commandParameters[1].equalsIgnoreCase(CONSTANTS.Record)) {
parent.startAudioRecording(commandParameters[2], true);
} else {
parent.playAudio(commandParameters[2], false);
}
case CONSTANTS.TTL:
this.m_isWifiConnected();
break;
case CONSTANTS.DELETE:
parent.deleteFile(commandParameters[1], true);
// deleteRecording(commandParameters[3],
// commandParameters[1]
// + commandParameters[2]);
// deleteEveryThing(Environment.getExternalStorageDirectory()
// .getAbsolutePath() + "/" + commandParameters[2]);
// deleteEveryThing(pathToSave + "/Response/" + course + "/"
// + commandParameters[1]);
break;
case CONSTANTS.RESTART:
StudentUtility.sendToEveryStudent(tempCommand);
StudentUtility.displayOnProjector(tempCommand);
parent.setStudentIPList(new HashMap<String, ArrayList<String>>());
parent.setCourse("");
break;
case CONSTANTS.EXIT:
StudentUtility.displayOnProjector(tempCommand);
StudentUtility.sendToEveryStudent(tempCommand);
break;
default:
break;
}
}
This is inside my Jar and I want users to be able to add any number of cases as functions in wrapper application. More over, preferably, I would like to put the existing cases in the same hash map used for registering commands and functions in wrapper application.
I need to know the correct design pattern and some advice how to go about it.
Thanks in advance.
Combine design pattern Command with a mapping of function code to commands. This has the benefit of moving the implementations to their own self-contained classes, which makes code more manageable and easier to test.
You might want to use Design Pattern Visitor instead of modifying the command map, but depends on your circumstances.
A common Interface for your commands
public Interface Command {
void action();
}
Create a bunch of concrete commands
public class Course implements Command {
private final String tempCommand;
private final ParentClass parent;
public Course(final String tempCommand, final ParentClass parent) {
this.tempCommand = tempCommand;
this.parent = parent;
}
public void action() {
parent.setCourse(tempCommand.split(parent.getDelimiter())[1]);
}
};
Map the preexisting function codes to commands.
private final Map<Integer, Command> getBuiltIns() {
Private final Map<Integer, Command> cmdMap = new HashMap<Integer, Command>();
cmdMap.add(CONSTANTS.COURSE, new Command(tempCommand, parent));
<etc>
return cmdMap;
}
private final cmdMap = getBuildIns();
Make a method that can add commands
public void addCommand(final int code, final Command command) {
// Check if code is already used, or there might be trouble
cmdMap.add(code, command);
}
Find the command then execute.
private void processCommand(String tempCommand) throws NumberFormatException, IOException, ELearningException
{
final Command theCommand = cmdMap.get(Integer.parseInt(commandParameters[0]));
theCommand.action();
}

Is there a way to take an argument in a callable method?

I have created a piece of code which takes an IP address (from main method in another class) and then loops through a range of IP addresses pinging each one as it goes. I have a GUI front end on this and it was crashing (hence why I've done the multithreading. My problem is I can no longer take the IP address as an argument in my ping code as its callable. I've searched all over for this and cant seem to find a way to get round this. Is there a way for a callable method to take arguments? If not is there any other way to accomplish what I'm trying to do?
sample of my code:
public class doPing implements Callable<String>{
public String call() throws Exception{
String pingOutput = null;
//gets IP address and places into new IP object
InetAddress IPAddress = InetAddress.getByName(IPtoPing);
//finds if IP is reachable or not. a timeout timer of 3000 milliseconds is set.
//Results can vary depending on permissions so cmd method of doing this has also been added as backup
boolean reachable = IPAddress.isReachable(1400);
if (reachable){
pingOutput = IPtoPing + " is reachable.\n";
}else{
//runs ping command once on the IP address in CMD
Process ping = Runtime.getRuntime().exec("ping " + IPtoPing + " -n 1 -w 300");
//reads input from command line
BufferedReader in = new BufferedReader(new InputStreamReader(ping.getInputStream()));
String line;
int lineCount = 0;
while ((line = in.readLine()) != null) {
//increase line count to find part of command prompt output that we want
lineCount++;
//when line count is 3 print result
if (lineCount == 3){
pingOutput = "Ping to " + IPtoPing + ": " + line + "\n";
}
}
}
return pingOutput;
}
}
IPtoPing used to be the argument that was taken.
You can't pass it as the argument to call() because the method signature doesn't allow it.
However, you can pass the necessary information as a constructor argument; e.g.
public class DoPing implements Callable<String>{
private final String ipToPing;
public DoPing(String ipToPing) {
this.ipToPing = ipToPing;
}
public String call() throws SomeException {
InetAddress ipAddress = InetAddress.getByName(ipToPing);
....
}
}
(I've corrected a couple of egregious code style violations!!)
There are ways to eliminate some of the "boilerplate" coding in the above (see some of the other answers). In this case we are talking about 4 lines of code (in a ~40 line class), so I am not convinced that it is worth the effort. (But hey, it is your code.)
Alternatively, you could:
declare DoPing as an inner class (or a lambda) and have it refer to a final ipToPing in the enclosing scope, or
add a setIpToPing(String ipToPing) method.
(The last allows a DoPing object to be reused, but the downside is that you will need to synchronize to access it thread-safely.)
Adding to Jarle's answer -- in case you create Callable as instance of anonymous class, you can use final field outside of anonymous class for passing data into the instance:
final int arg = 64;
executor.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return arg * 2;
}
});
You can't pass arguments to call() because the method signature doesn't allow it but here is at least one way to work around that by
defining an abstract class that wraps/implements Callable and
implementing a setter to "inject" a result into call()
Define an abstract class:
import java.util.concurrent.Callable;
public abstract class Callback<T> implements Callable<Void> {
T result;
void setResult (T result) {
this.result = result;
}
public abstract Void call ();
}
Define the method that should fire the callback:
public void iWillFireTheCallback (Callback callback) {
// You could also specify the signature like so:
// Callback<Type of result> callback
// make some information ("the result")
// available to the callback function:
callback.setResult("Some result");
// fire the callback:
callback.call();
}
In the place where you want to call iWillFireTheCallback:
Define the callback function (even possible inside methods):
class MyCallback extends Callback {
#Override
public Void call () {
// this is the actual callback function
// the result variable is available right away:
Log.d("Callback", "The result is: " + result);
return null;
}
}
And then call iWillFireTheCallback while passing in the callback:
iWillFireTheCallback(new MyCallback());
When you create the doPing-class (should be captial letter in class name), send in the ip-address in the constructor. Use this ip-address in the call-method.
Put some (final) fields in your doPing class, and a constructor that initializes them, then pass the values you want to use in call() to the constructor of doPing:
public class DoPing implements Callable<String> {
private final String ipToPing;
public DoPing(String ip) {
this.ipToPing = ip;
}
public String call() {
// use ipToPing
}
}
I know it is super-late to answer this, considering it is more than 8 years old but active 15 days(!) ago, I feel this will still help someone using Java 8 and above.
PS, it is simply a syntactic sugar of Victor Sorokin's answer possible through lambdas.
public static Callable<String> generateCallableWithArg(final String input) {
return () -> {
Thread.sleep(5000); // someExpensiveOperationHere
return "Return Value of " + input; //input can be used here
};
}
Also, we can write a static helper method that can convert a Function to Callable.
public class CallableGenerator {
public static <T,V> Callable<V> getCallableFromFunction(Function<T, V> function, T input) {
return () -> function.apply(input);
}
}
This can be used as
Callable<Integer> iAmCallable = CallableGenerator.getCallableFromFunction(i1 -> i1 * 2, 3);
You have to defien a property such as ipAddress and its accessor method. and passing its value in constructor or by setter method. In doPing class use ipAddress property.
class DoPing/* In java all classes start with capital letter */implements Callable<String>
{
private String ipAddress;
public String getIpAddress()
{
return ipAddress;
}
public void setIpAddress(String ipAddress)
{
this.ipAddress = ipAddress;
}
/*
* Counstructor
*/
public DoPing(String ipAddress )
{
this.ipAddress = ipAddress;
}
#Override
public String call() throws Exception
{
// your logic
}
}
It is not always possible to make reference to (effectively) final variable to use its value as "argument", but you can make comfy general solution by yourself. First define this functional interface:
#FunctionalInteface
interface CallableFunction<T, R> {
public abstract R call(T arg) throws Exception;
public static <T, R> Callable<R> callable(CallableFunction<T, R> cf, T arg) {
return () -> cf.call(arg);
}
}
This functional interface provides static method callable that creates a Callable instance, which simply calls call(T) with provided argument (of type T). Then you need you DoPing class to implement CallableFunction like this:
public class DoPing implements CallableFunction<String, String> {
#Override
public String call(final String ipToPing) throws Exception {
final var ipAddress = InetAddress.getByName(ipToPing);
final var reachable = ipAddress.isReachable(1400);
String pingOutput = null;
if (reachable) {
pingOutput = ipToPing + " is reachable.\n";
}
else {
final var ping = Runtime.getRuntime().exec("ping " + ipToPing + " -n 1 -w 300");
try (var in = new BufferedReader(new InputStreamReader(ping.getInputStream()))) {
String line;
for (int lineCount = 1; (line = in.readLine()) != null; ++lineCount) {
if (lineCount == 3) {
pingOutput = "Ping to " + ipToPing + ": " + line + "\n";
break;
}
}
}
}
return pingOutput;
}
Here we changed call signature to accept String argument and also now it implements CallableFunction and not Callable as before. Other changes are minor, but it's worth mentioning, that we prevented resource leak with use of try-with-resource on BufferedReader and also break has been added to input collecting loop (change from while to for) to terminate as quickly, as possible.
Now you can use the code e.g. like this:
final var ping = CallableFunction.callable(new DoPing(), "127.0.0.1");
final var task = new FutureTask<>(ping);
new Thread(task).start();
System.out.println(task.get(20, TimeUnit.SECONDS));
You can also reuse CallableFunction in other cases, when you needed it.

Better way to write this Java code? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
public void handleParsedCommand(String[] commandArr) {
if(commandArr[0].equalsIgnoreCase("message")) {
int target = Integer.parseInt(commandArr[1]);
String message = commandArr[2];
MachatServer.sendMessage(target, this.conId, message);
} else if(commandArr[0].equalsIgnoreCase("quit")) {
// Tell the server to disconnect us.
MachatServer.disconnect(conId);
} else if(commandArr[0].equalsIgnoreCase("confirmconnect")) {
// Blah blah and so on for another 10 types of command
} else {
try {
out.write("Unknown: " + commandArr[0] + "\n");
} catch (IOException e) {
System.out.println("Failed output warning of unknown command.");
}
}
}
I have this part of my server code for handling the types of messages. Each message contains the type in commandArr[0] and the parameters in the rest of commandArr[]. However, this current code, while working seems very unelegant. Is there a better way to handle it? (To the best of my knowledge, String values can't be used in switch statements, and even then, a switch statement would only be a small improvement.
I'd refactor this using the Command Design Pattern.
Basically each of your commands, message, quit, confirmconnect and a default will have a class implementation and will implement the Command Interface.
/*the Command interface*/
public interface ICommand {
void execute(String[] commandArr);
}
public class Message implements ICommand {
void execute(String[] commandArr) {
int target = Integer.parseInt(commandArr[1]);
String message = commandArr[2];
MachatServer.sendMessage(target, this.conId, message);
}
}
//same type of class for other commands
public class CommandManager {
public ICommand getCommand(String commandName) {
//store all the commands in a hashtable.
//Pull them out by name as requested.
}
//Esko's suggestion from comments
public static void executeImmediately(String[] commandArr) {
getCommand(commandArr[0]).execute(commandArr);
}
}
public void handleParsedCommand(String[] commandArr) {
ICommand command = CommandManager.getCommand(commandArr[0]);
command.execute(commandArr);
//or Esko
CommandManager.executeImmediately(commandArr);
}
Here are two variants using enums that (nearly) provide the same behavior in a much more readable way:
1) Enums for a type-safe switch:
enum CommandType {
MESSAGE,
QUIT,
CONFIRMCONNECT
}
public void handleParsedCommand(String[] commandArr) {
CommandType cmd = null;
try {
cmd = CommandType.valueOf(commandArr[0].toUpperCase());
} catch (IllegalArgumentException e) {
// this kind of error handling, seems a bit strange, by the way.
try {
out.write("Unknown: " + commandArr[0] + "\n");
} catch (IOException e) {
System.out.println("Failed output warning of unknown command.");
}
return;
}
switch(cmd) {
case MESSAGE:
int target = Integer.parseInt(commandArr[1]);
String message = commandArr[2];
MachatServer.sendMessage(target, this.conId, message);
case QUIT:
// Tell the server to disconnect us.
MachatServer.disconnect(conId);
case CONFIRMCONNECT:
// Blah blah and so on for another 10 types of command
}
}
}
The main benefits are that the code is more readable, but you avoid creating new methods or classes for each of the cases, which is not allows what you want if the handling code has only one or two lines.
2) Another enum-based variant, that is in fact a Command pattern, but which out much bloat code:
enum CommandType {
MESSAGE {
void execute(CommandProcessor cp, String[] params) {
int target = Integer.parseInt(params[1]);
String message = params[2];
MachatServer.sendMessage(target, cp.conId, message);
}
},
QUIT {
void execute(CommandProcessor cp, params param) {
MachatServer.disconnect(cp.conId);
}
},
CONFIRMCONNECT {
void execute(CommandProcessor cp, params param) {
// Blah blah and so on for another 10 types of command
}
};
abstract void execute(CommandProcessor cp, String[] param);
}
public void handleParsedCommand(String[] commandArr) {
CommandType cmd = null;
try {
cmd = CommandType.valueOf(commandArr[0].toUpperCase());
} catch (IllegalArgumentException e) {
try {
out.write("Unknown: " + commandArr[0] + "\n");
} catch (IOException e) {
System.out.println("Failed output warning of unknown command.");
}
return;
}
cmd.execute(this, commandArr);
}
Yeap, looks like a Command + Prototype pattern to me.
In the command you define what is going to be done, and the prototype is to place an instance of each command in a lookup table and "clone" them to be executed each time.
The refactoring would be like:
Before:
public void handleParsedCommand(String[] commandArr) {
if(commandArr[0].equalsIgnoreCase("message")) {
int target = Integer.parseInt(commandArr[1]);
String message = commandArr[2];
MachatServer.sendMessage(target, this.conId, message);
} else if(commandArr[0].equalsIgnoreCase("quit")) {
// Tell the server to disconnect us.
MachatServer.disconnect(conId);
} else if(commandArr[0].equalsIgnoreCase("confirmconnect")) {
// Blah blah and so on for another 10 types of command
} else {
try {
out.write("Unknown: " + commandArr[0] + "\n");
} catch (IOException e) {
System.out.println("Failed output warning of unknown command.");
}
}
}
After:
public void handleParsedCommand(String[] commandArr) {
Command.getCommand( commandArr ).execute();
}
// Define the command and a lookup table
abstract class Command {
// Factory using prototype
public static Command getCommand( String [] commandArr ) {
// find the handling command
Command commandPrototype = commandMap.get( commandArr[0] );
// if none was found, then use "uknown"
if ( commandPrototype == null ) {
commandPrototype = commandMap.get("unknown");
}
// Create an instance using clone
Command instance = commandPrototype.clone();
instance.args = commanrArr;
return instance;
}
// lookup table ( switch substitute )
private static Map<String,Command> commandsMap = new HashMap()<String,Command>(){{
put("message" , new MessagCommand());
put("quit" , new QuitCommand());
put("confirmconnect", new ConfirmConnectCommand());
...
put("unknow" , new UnknownCommand());
}};
// args of the command
private String [] args;
public void execute();
String [] getArgs(){
return this.args;
}
}
And the provide the specific implementations
class MessageCommand extends Command {
public void execute(){
int target = Integer.parseInt(commandArr[1]);
String message = commandArr[2];
MachatServer.sendMessage(target, this.conId, message);
}
}
class MessageCommand extends Command {
public void execute(){
int target = Integer.parseInt(getArgs()[1]);
String message = getArgs()[2];
MachatServer.sendMessage(target, this.conId, message);
}
}
class QuitCommand extends Command {
public void execute() {
MachatServer.disconnect(conId);
}
}
class ConfirmConnectCommand extends Command {
public void execute() {
/// blah blah blah
}
}
class UnknowCommand extends Command {
public void execute() {
try {
out.write("Unknown: " + commandArr[0] + "\n");
} catch (IOException e) {
System.out.println("Failed output warning of unknown command.");
}
}
}
// ... other 10 implementations here...
Take a look at Commons CLI which is a command-line argument parser.
Here are some examples of its usage.
You can use enums
For starters, I would make a map between the commands and a class which executes each type of command (say an anonymous class that implements a known interface) and then retrieve the right class from the map, and then passes it the rest of the parameters.
If it made sense, you could use an enum here with a static method to retrieve the right one, that way you could switch if and when you needed to (say you had to do the same thing on 5 of the 10 commands).
First of all you are reading the same element of the array every time. This should be the first thing to factor out. equalsIgnoreCase is a bit long, so normalise the case first (don't pick up the default locale!).
It is possible to use enums to hack a switch of Swtings. JDK7 may include a switch on String, IIRC.
I like Bob's answer. Another method would be to use the Spring framework and the IoC functionality. Basically, I've done this before to use Spring (inflates from xml) to create a Map where you have each command object stored with a key. The key would be the same as the text in commandArr[0].
So your xml looks something like
<property name="commands">
<map>
<entry>
<key>
<value>message</value>
</key>
<ref bean="messageCommand" />
</entry>
</map>
</property>
<bean id="messageCommand" ref="org.yourorg.yourproject.MessageCommand" />
And then in your code...
commands.get(commandArr[0]).execute() //or whatever
This allows you to not run any sort of initialization code. All you have to do is inflate the xml. Spring handles populating the map for you. Also, you can define any necessary data members in the classes using a similar syntax. Also, if you need to add functionality, all you have to do is change the xml rather than mucking with and recompiling code. I'm personally a huuuuge fan :)
For more info, check out this article for a brief overview of IoC and then check out this article for the documentation

Categories