Let's say I'm writing a bot for a chat (discord, telegram, whatever). The bot can handle chat commands (e.g. !join tells it to join a voice channel on the server).
So somewhere in my code I'd have to parse the command, and I'll have something like
String userMessage = getTheMessageTextSomehow();
// Do something with the message.
I'd like to have a Command class for every one of my commands, and every command would implement a execute() method.
My question is: what's the best practice to create those command objects?
The easiest way would be to have a large CommandFactory or whatever class somwhere, that would be like
if(message.equals(JOIN_MESSAGE) {
return new JoinCommand();
}
if(message.equals(LEAVE_MESSAGE){
return new LeaveCommand();
}
//etc...
That looks like a bad practice and code smell to me.
Is there a better way to do it?
You might want to rely on a Map of Commands.
I'll make it clear that for this usecase, using the Function or Supplier, or whatever standard functional interface is not idiomatic at all. Avoid it.
We can start by building a Command interface
interface Command {
Result execute();
}
Or if you need to accept an argument
interface Command {
Result execute(final Input input);
}
Which will have the required implementations
class JoinCommand implements Command { ... }
class LeaveCommand implements Command { ... }
class NoopCommand implements Command { ... }
And so on.
You'll now need to store those definitions in a key (the command) - value (the implementation) data structure. A Map is perfect for that.
As your command definition will be a String, then
static final Map<String, Command> COMMANDS = new HashMap<>(8);
static {
COMMANDS.put("join", new JoinCommand());
COMMANDS.put("leave", new LeaveCommand());
// And so on
}
The usage is pretty simple
final String userMessage = getTheMessageTextSomehow();
final String commandStr = extractCommand(userMessage);
final Command command = COMMANDS.getOrDefault(commandStr, NOOP_COMMAND);
command.execute();
Or if you'll have to accept an argument
command.execute(yourInput);
You'll also notice I used NOOP_COMMAND, that's just a no-op implementation for Command to avoid dealing with null. It might be, or it might be not, appropriate.
If you're on Java 9+, the Map could also be created using
Map.of(
"join", new JoinCommand(),
"leave", new LeaveCommand(),
// And so on.
)
Usually, it is implemented via mapping. It would be much clearer and readable to implement this with simple Map.
For example:
Map<String, Command> strategies = new HashMap<String, Command>(){{
put(JOIN_MESSAGE, new JoinCommand());
put(LEAVE_MESSAGE, new LeaveCommand());
}};
And its usage:
Command command = strategies.get(messageType);
Moreover, you can define creation strategies (factories) since Java 8 if you need to construct commands depending on some parameters.
Map<String, Function<String, Command>> strategies = new HashMap<String, Command>(){{
put(JOIN_MESSAGE, param -> new JoinCommand(param)); // or JoinCommand::new
put(LEAVE_MESSAGE, param -> new LeaveCommand(param)); // or LeaveCommand::new
}};
And its usage:
Command command = strategies.get(messageType);
command.process(param);
Hello You try Switch Case statement for it, it's easy to understand and in future if you have any changes then it's easy to update the code.
switch(message)
{
case JOIN_MESSAGE:
return new JoinCommand();
break;
case LEAVE_MESSAGE:
return new LeaveCommand();
break;
}
Related
I am making a multiplayer game which makes heavy use of a serialisable Event class to send messages over a network. I want to be able to reconstruct the appropriate subclass of Event based on a constant.
So far I have opted for the following solution:
public class EventFactory {
public static Event getEvent(int eventId, ByteBuffer buf) {
switch (eventId){
case Event.ID_A:
return EventA.deserialise(buf);
case Event.ID_B:
return EventB.deserialise(buf);
case Event.ID_C:
return EventC.deserialise(buf);
default:
// Unknown Event ID
return null;
}
}
}
However, this strikes me as being very verbose and involves adding a new 'case' statement every time I create a new Event type.
I am aware of 2 other ways of accomplishing this, but neither seems better*:
Create a mapping of constants -> Event subclasses, and use clazz.newInstance() to instantiate them (using an empty constructor), followed by clazz.initialiase(buf) to supply the necessary parameters.
Create a mapping of constants -> Event subclasses, and use reflection to find and call the right method in the appropriate class.
Is there a better approach than the one I am using? Am I perhaps unwise to disregard the alternatives mentioned above?
*NOTE: in this case better means simpler / cleaner but without compromising too much on speed.
You can just use a HashMap<Integer,Event> to get the correct Event for the eventID. Adding or removing events is going to be easy, and as the code grows this is easy to maintain when compared to switch case solution and speed wise also this should be faster than switch case solution.
static
{
HashMap<Integer,Event> eventHandlerMap = new HashMap<>();
eventHandlerMap.put(eventId_A, new EventHandlerA());
eventHandlerMap.put(eventId_B, new EventHandlerB());
............
}
Instead of your switch statement Now you can just use :
Event event = eventHandlerMap.get(eventId);
if(event!=null){
event.deserialise(buf);
}
If you're not afraid of reflection, you could use:
private static final Map<Integer, Method> EVENTID_METHOD_MAP = new LinkedHashMap<>();
static {
try {
for (Field field : Event.class.getFields())
if (field.getName().startsWith("ID_")) {
String classSuffix = field.getName().substring(3);
Class<?> cls = Class.forName("Event" + classSuffix);
Method method = cls.getMethod("deserialize", ByteBuffer.class);
EVENTID_METHOD_MAP.put(field.getInt(null), method);
}
} catch (IllegalAccessException|ClassNotFoundException|NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Event getEvent(int eventId, ByteBuffer buf)
throws InvocationTargetException, IllegalAccessException {
return (Event) EVENTID_METHOD_MAP.get(eventId).invoke(null, buf);
}
This solution requires that int ID_N always maps to class EventN, where N can be any String where all characters return true for the method java.lang.Character.isJavaIdentifierPart(c). Also, class EventN must define a static method called deserialize with one ByteBuffer argument that returns an Event.
You could also check if field is static before trying to get its field value. I just forget how to do that at the moment.
I'm writing a program that needs to have Command Objects. A Command contains a String for its name, and an AbstractAction that represents what the Command actually does. Furthermore, a Command has a method, init(), used higher up in the program's hierarchy that instantiates variables for the Command's use (to provide access to the GUI, network, and so on), and a method, execute(), that executes the AbstractAction on a special Thread. Here is an example of creating and using a Command:
Command c = new Command("Test",
new AbstractAction() {
public void actionPerformed(ActionEvent a) {
System.out.println("Hello world!");
}
});
At this point, calling "c.execute();" will print out "Hello world!", as expected.
My goal is to have a text file with pairs of values, which can be parsed to generate a String name and an AbstractAction action. Once that has been done, another class will go through the found names and actions, create Command Objects for each one, and add them to the list of commands in the program, where they can then be used as normal.
Right now, my problem is that I read in a String that represents the body of the private AbstractAction above- but there isn't an easy way to actually convert the String into an actual AbstractAction Object.
One potential idea was creating a temporary java file with the AbstractAction String representation, compiling it, creating a new AbstractAction from it, and then get that reference using reflection, but that seems like overkill. Another was to directly modify the source of the file that parses through the file, so that it would have the code of the AbstractAction written out, but again, this is a bit crazy.
I've tried a few other implementations, including forcing the user to create a subclass of Command, putting their source into a special program folder, and then creating the Commands on initialisation, but this ended up being a lot of work for the user (lots of redundant code).
Please let me know if there's a better way to implement what I want to do- or if there's an easier way to turn the String of the source into an inner Object as above.
Edit 1:
Here is an example of what the text file would look like:
//Anything outside of quotes is a comment
"Foo", "System.out.println("Hello world!");"
"Bar", "network.sendOverAFile(new File("test.txt"));"
From here, the parser (on startup) would read through the file and extract "Foo" as a String name, and "System ... ;" as a String action. I need to turn action into the code in the body of the AbstractAction, as seen above when creating the Command.
The same would be done for Bar; Bar uses one of the variables passed by init().
As for the subclass implementation I tried, the user would have to create their own subclass of Command, and put it into a source folder. A subclass would look something like this:
public class TestCommand extends Command {
public TestCommand() {
super("Test", new AbstractAction() {
public void actionPerformed(ActionEvent a) {
System.out.println("Hello!");
}
});
}
}
This would then be put into a source directory, among every other subclassed Command, and compiled. The parser would go through the compiled code segments, and add the relevant information to an array. Every time a Command would normally be executed, the parser scans through the list of all names, and if there is a match, execute the relevant AbstractAction. This works, but involves a ton of references to external classes (which will probably slow down the program with dozens of commands), and is two or three times as much work for the users making the plugin. As a result, I felt it would be much easier to use the text file technique above, but I don't know how to turn a String representation of the code into the code itself; Ergo my initial question.
This sounds like a case of overengineering. Do you really need this much flexibility at runtime, or do you simply have a lot of commands and you want an easy way to refer to them in a file?
If it's the latter, your text file doesn't need to contain the code; it just needs to contain symbolic identifiers corresponding to that code. Those identifiers should exist in your code as enum constants:
public enum Command { FOO, BAR }
You should create all of your actions in code, and place those actions in a Map using the enum constants as keys. Your file can then refer to the actions by those enum constants:
public List<Action> parseActions(Path file)
throws IOException {
List<Action> actions = new ArrayList<>();
try (BufferedReader reader =
Files.newBufferedReader(file, Charset.defaultCharset())) {
String line;
while ((line = reader.readLine()) != null) {
Command command = Command.valueOf(line);
Action action = getAction(command);
actions.add(action);
}
}
return actions;
}
private Map<Command, Action> allActions;
private Action getAction(Command command) {
Objects.requireNonNull(command, "Command cannot be null");
if (allActions == null) {
allActions = new EnumMap<>(Command.class);
allActions.put(Command.FOO, new AbstractAction() {
public void actionPerformed(ActionEvent event) {
System.out.println("Hello world!");
}
};
allActions.put(Command.BAR, new AbstractAction() {
public void actionPerformed(ActionEvent event) {
network.sendOverAFile(new File("test.txt"));
}
};
// Safety check
if (!allActions.keySet().containsAll(
EnumSet.allOf(Command.class))) {
throw new RuntimeException(
"Not every Command constant has an associated Action");
}
}
return allActions.get(command);
}
To conform to the above, your text file would simply contain:
FOO
BAR
If you really and truly need fully dynamic code that can be read from a text file, bear in mind that it is a tremendous security hole. In fact, it is the very definition of code injection: anyone can place arbitrary code (including things like Runtime.getRuntime().exec("rd /s/q C:\\Windows\\System32") or Runtime.getRuntime().exec("rm -rf ~")) in a file and your program will gladly run it.
If you're still sure that you want to do it, you'd probably want to use the JavaScript engine that comes with every Java runtime:
public List<Action> parseActions(Path file)
throws IOException {
List<Action> actions = new ArrayList<>();
final ScriptEngine engine =
new ScriptEngineManager().getEngineByName("JavaScript");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("network", myNetwork);
try (BufferedReader reader =
Files.newBufferedReader(file, Charset.defaultCharset())) {
String line;
while ((line = reader.readLine()) != null) {
String[] nameAndCode = line.split("\\s+", 2);
String name = nameAndCode[0];
final String code = nameAndCode[1];
Action action = new AbstractAction() {
public void actionPerformed(ActionEvent event) {
engine.eval(code);
}
};
actions.add(action);
}
}
return actions;
}
Each line in your file would contain a command name followed by JavaScript code. So it might look like this:
Foo importClass(java.lang.System); System.out.println('Hello world!');
Bar importClass(java.io.File); network.sendOverAFile(new File('test.txt'));
Another major disadvantage of doing this, in my opinion, is that the code won't benefit from compiler checks, and you certainly can't set breakpoints in that code from a debugger. All in all, it will be a considerable headache to debug and maintain.
I want to make a simple interative shell based on the console where I can write commands like login, help, et cetera.
I first thought of using Enums, but then I didn't know how to implement them neatly without a load of if-else statements, so I decided to go with an array-approach and came up with this:
public class Parser {
private static String[] opts = new String[] {"opt0", "opt1", "opt2", "opt3" ... }
public void parse(String text) {
for(int i = 0; i < opts.length; i++) {
if(text.matches(opts[i]) {
switch(i) {
case 0:
// Do something
case 1:
// Do something-something
case 2:
// Do something else
}
return;
}
}
}
}
But I ended up seeing that this was probably the most rudimentary way of doing something like this, and that there would be problems if I wanted to change the order of the options. How could I make a simpler parser? This way it would work, but it would also have said problems. The use of the program is purely educational, not intended for any serious thing.
A simple approach is to have a HashMap with the key equal to the command text and the value is an instance of class that handle this command. Assuming that the command handler class does not take arguments (but you can easily extend this) you can just use a Runnable instance.
Example code:
Runnable helpHandler = new Runnable() {
public void run(){
// handle the command
}
}
// Define all your command handlers
HashMap<String, Runnable> commandsMap = new HashMap<>(); // Java 7 syntax
commandsMap.put("help",helpHandler);
// Add all your command handlers instances
String cmd; // read the user input
Runnable handler;
if((handler = commandsMap.get(cmd)) != null) {
handler.run();
}
You can easily extend this approach to accept argument by implementing your own interface and subclass it. It is good to use variable arguments if you know the data type e.g. void execute(String ... args)
One solution that comes to mind is actually using Design patterns. You could use the input from the user, as the discriminator for a Factory class.
This factory class will generate an object, with an "execute" method, based on the input. This is called a Command object.
Then you can simply call the method of the object returned from the factory.
No need for a switch statement. If the object is null, then you know the user entered an invalid option, and it abstracts the decision logic away from your input parser.
Hopefully this will help :)
I have a about 20 different services which I have to send requests to which require a slightly different set of headers.
The bad legacy code is something like this,
row = db.query_for_service()
if row.type == 'foo1'
// add common headers to request
// add foo1 specific headers 1
// add foo1 specific header 2
// add foo1 specific header 3
else if row.type == 'foo2'
// add common headers to request
// add foo2 specific header 1
...
...
...
else if row.type == foo20
// add common headers to request
// add foo20 specific header 1
// add foo20 specific header 2
// ...
send_request()
What is the best way to refactor this? I have considered some patterns that may work here (strategy, builder) but I am not too sure.
I am currently learning both Java and Python and I would to get thoughts on how the solutions would differ in the two languages
Personally, what I would do is something along these lines.
#Put this in the initialisation
Map foos<row.type,String> = new Map<row.type, String>()
#Populate the map
map.set('a') = 'headerA specific params x=1'
map.set('b') = 'headerB specific params x=2'
map.set('c') = 'headerC specific params y=3'
map.set ...
Map bars<String,String> = new Map<String,String()
bars.set('fooA') = 'a,b'
bars.set('fooB') = 'a,c'
String commonheader = "HTTP/1.1"
#This would be in a method
row = db.query_for_service()
String output_header += commonheader
for i in bars.get(fooN).split(','):
output_header += foos.get(i)
send_request()
In sort of pseudo java/python. The map would be pre-filled with everything you need, then just pick out what you want and attach.
You should try pattern Command.
The pseudo code something like this:
interface Command(){
void execute();
}
class ConcreteCommandA() implements Command {
#Override
void execute(){
// action 1
}
}
class ConcreteCommandB() implements Command {
#Override
void execute(){
// action 2
}
}
and use this structure in your client:
Map<String, Command> commands = new HashMap<String, Command>;
commands.put("action1", new ConcreteCommandA());
commands.put("action2", new ConcreteCommandB());
runCommand(String str){
Command command = commands.get(str);
command.execute();
}
and so on
For readability reasons I'm trying to avoid using Char based case constructs, using Java 6. I cannot switch to 7 jet...
Map<String, String> map = new HashMap<String, String>() {
{
put("foo", "--foo");
put("bar), "--bar");
...
}
private static final long serialVersionUID = 1L; // java problem
};
The serialVersionUID - as far as I know, maybe part of the problem. Currently I'm working with if constructs:
if (!map.containsValue(args[0])) {
logger.error("Unknown parameter: " + args[0]);
...
I handle ~ 30 parameters. In any case a growing number.
Is it even possible to define switch constructs with enums or HashMaps In Java 6?
If you're handling over 30 parameters in the same way, then you need some kind of loop. For example:
for (int i=0; i<args.length; i++)
{
String param = args[i];
if (!map.containsValue(param))
logger.error("Unknown parameter: " + param);
.. handle argument
}
It looks like you are parsing command line arguments. There are some good libraries available that offer flexible command line parsing, for example args4j. With args4j, you create your data model, and let it map fields in the data to command line arguments.
Using Strings in a switch statement will be available in Java 7.
For moderate or complex parsing of command line arguments I strongly recommend using Commons-CLI, it provides a great API to make this much easier for you to handle. An example of it's usage:
// create Options object
Options options = new Options();
// add t option
options.addOption("t", false, "display current time");
...
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse( options, args);
if(cmd.hasOption("t")) {
// print the date and time
}
else {
// print the date
}