skip to Main Content

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?

3

Answers


  1. 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.
    )
    
    Login or Signup to reply.
  2. 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);
    
    Login or Signup to reply.
  3. 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;
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search