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

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();
}

Related

Best Java design pattern to handle console commands

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

Dynamically invoke the correct implementation in a factory

I have a library which parse URLs and extract some data. There is one class per URL. To know which class should handle the URL provided by the user, I have the code below.
public class HostExtractorFactory {
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
switch (host) {
case HostExtractorABC.HOST_NAME:
return HostExtractorAbc.getInstance();
case HostExtractorDEF.HOST_NAME:
return HostExtractorDef.getInstance();
case HostExtractorGHI.HOST_NAME:
return HostExtractorGhi.getInstance();
default:
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
}
The problem is users are requesting more URL to be parsed, which means my switch statement is growing. Every time someone comes up with a parser, I have to modify my code to include it.
To end this, I've decided to create a map and expose it to them, so that when their class is written, they can register themselves to the factory, by providing the host name, and the extractor to the factory. Below is the factory with this idea implemented.
public class HostExtractorFactory {
private static final Map<String, HostExtractor> EXTRACTOR_MAPPING = new HashMap<>();
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
if(EXTRACTOR_MAPPING.containsKey(host)) {
return EXTRACTOR_MAPPING.get(host);
} else {
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
public static void register(String hostname, HostExtractor extractor) {
if(StringUtils.isBlank(hostname) == false && extractor != null) {
EXTRACTOR_MAPPING.put(hostname, extractor);
}
}
}
And the user would use it that way:
public class HostExtractorABC extends HostExtractor {
public final static String HOST_NAME = "www.abc.com";
private static class HostPageExtractorLoader {
private static final HostExtractorABC INSTANCE = new HostExtractorABC();
}
private HostExtractorABC() {
if (HostPageExtractorLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
HostExtractorFactory.register(HOST_NAME, this);
}
public static HostExtractorABC getInstance() {
return HostPageExtractorLoader.INSTANCE;
}
...
}
I was patting my own back when I realized this will never work: the user classes are not loaded when I receive the URL, only the factory, which means their constructor never runs, and the map is always empty. So I am back to the drawing board, but would like some ideas around getting this to work or another approach to get rid of this pesky switch statement.
S
Another option is to use the Service Loader approach.
Having your implementers add something like the following in ./resources/META-INF/services/your.package.HostExtractor:
their.package1.HostExtractorABC
their.package2.HostExtractorDEF
their.package3.HostExtractorGHI
...
Then in your code, you can have something like:
HostExtractorFactory() {
final ServiceLoader<HostExtractor> loader
= ServiceLoader.load(your.package.HostExtractor.class);
for (final HostExtractor registeredExtractor : loader) {
// TODO - Perform pre-processing which is required.
// Add to Map? Extract some information and store? Etc.
}
}
I would advice for you to learn about dependency injection (I love spring implementation). You will then be able to write an interface like
public interface HostExtractorHandler {
public String getName();
public HostExtractor getInstance();
}
Than your code can "ask" for all classes that implements this interface, you then would be able to build your map in the initialization phase of your class.
I would use the Reflections library to locate the parsers. They all appear to derive from the HostExtractor class, so use the library to locate all subtypes:
Reflections reflections = new Reflections("base.package");
Set<Class<? extends HostExtractor>> extractorTypes =
reflections.getSubTypesOf(HostExtractor.class);
Use the results to create the instances in your factory:
for (Class<? extends HostExtractor> c : extractorTypes) {
HostExtractor he = c.newInstance();
EXTRACTOR_MAPPING.put(he.getHostName(), he);
}
I made up the getHostName method, but it should be trivial to add to the HostExtractor base class.

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>.

Elegant way to parse console commands in java

So im making an application that works via console commands (think of "database new db_name"). Whats the most elegant way to handle this sort of thing?
Heres my little prototype, basicly just tons of switches. I mean it works but it gets kinda messy once I want to add stuff like putting out all the parameters if you pass a wrong one or too few, so im wondering if theres a better option:
public static void main(String[] args) {
while (running) {
Scanner in = new Scanner(System.in);
String cmd = in.nextLine();
parseCmd(cmd);
}
}
private static void parseCmd(String s) {
String[] cmd = s.split(" ");
try {
switch (cmd[0]) {
case "exit":
running = false;
System.out.println("Shutting down...");
break;
case "database":
switch (cmd[1]) {
case "new":
databases.add(new Database(cmd[2]));
break;
default:
System.out.println("Unknown parameter");
break;
}
break;
default:
System.out.println("Unknown command");
break;
}
} catch (Exception ArrayIndexOutOfBoundsException) {
System.out.println("Too few arguments passed");
}
}
I don't know about the most elegant way, but I've done something like this a couple of times and came up with this solution.
Make a class for "database" that represents the command, once you find the string "database", you retrieve an instance of this class and pass it the remaining arguments, so it can do the rest of the parsing.
class DataBaseCommand implements Command {
#Override
public void eval(String[] args) {...}
}
You can then keep a static map of these commands mapped to their names:
private static final Map<String, Command> commands = new HashMap<>();
static {
commands.put("database", new DataBaseCommand());
}
switch (cmd[0]) {
case "exit":
running = false;
System.out.println("Shutting down...");
break;
default:
if(commands.contains(cmd[0])) {
commands.get(cmd[0]).eval(cmd);
} else {
System.out.println("Unknown command");
}
break;
}
Maybe you could use a library that does a lot of those command parsing for you, like joptsimple?
I would suggest using http://jcommander.org/.Its simple and elegant. In it you can use inbuilt annotations
e.g Create a class like this using jcommander annotaions
#Parameters(commandNames = {"test", "Manages dates"})
public class CommandOption {
#Parameter(names = {"-e", "--exit"}, description = "bla bla")
public boolean exit = false;
}
then in main method
public static void main(String... args){
CommandOption opt = new CommandOption();
JCommander jc = new JCommander(opt);
jc.parse(args);
//and now use
System.out.println("value" +opt.exit)
}
and then can pass parmaeters as java class-e true or maybe better if you can create jar and then java -jar 'jar' -e true

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