Command Design Pattern

0

Command design pattern decouples the object that invokes the operation from the one that actually performs it. The one which executes the command may depend on another object for its actual execution also called receiver of the command.

In the below example, we build a set of commands to control the application externally using commands.

All the commands implement a common interface “Command”. For example sake, we have implemented three set of commands – to extract info about the application, add new consumers and finally a stop command to stop the application. CommandRegistry builds the commands, sets up the receivers.

The Command interface executes the command. It also allows one to pass in arguments so that the individual commands can further customize their behaviour. The parse(tokens) should do extract the other parameters that is required to know how the action should be performed. For example, stop() should let the existing running tasks to be finished or can also be immediate.

Command:

package com.javarticles.patterns;

import java.util.List;
import java.util.Map;

public interface Command {
    default void parseAndExec(List<String> tokens) throws CommandException {
        parse(tokens);
        exec(tokens);
    }
    
    void exec(List<String> tokens) throws CommandException;

    void parse(List<String> tokens);

    default void addToMap(Map<String, Command> cmdMap) {
        cmdMap.put(getCommandStr(), this);
    }

    String getCommandStr();
}

Our first command is to print information about the application.
InfoCommand:

package com.javarticles.patterns;

import java.util.List;

public class InfoCommand implements Command {

    @Override
    public String getCommandStr() {
        return "info";
    }

    @Override
    public void exec(List<String> tokens) throws CommandException {
        System.out.println("Return Info");
    }

    @Override
    public void parse(List<String> tokens) {
    }

}

StopCommand relies on application instance App as it needs to stop the application so the command simply delegates the call to app.stop so App is the receiver of the command here.

StopCommand:

package com.javarticles.patterns;

import java.util.List;

public class StopCommand implements Command {
    private App app;
    
    public void setApp(App app) {
        this.app = app;
    }

    @Override
    public String getCommandStr() {
        return "stop";
    }

    @Override
    public void exec(List<String> tokens) throws CommandException {
        System.out.println("Execute stop command");
        app.stop();
    }

    @Override
    public void parse(List<String> tokens) {
        System.out.println("Stop " + tokens);
    }

}

AddCommand adds consumers which in turn depend on a ConsumerRepository. This is another receiver class that knows what is to be done.

AddCommand:

package com.javarticles.patterns;

import java.util.List;

public class AddCommand implements Command {
    private ConsumerRepository consumerRepository;

    public AddCommand(ConsumerRepository consumerRepository) {
        this.consumerRepository = consumerRepository;
    }

    @Override
    public void exec(List<String> tokens) throws CommandException {
        tokens.forEach((consumer)->consumerRepository.addConsumer(consumer));
    }

    @Override
    public void parse(List<String> tokens) {
    }

    @Override
    public String getCommandStr() {
        return "add";
    }

}

ConsumerRepository can be someone that either directly interacts with a database or can be a client interacting with a server that publishes the consumer info to it.

ConsumerRepository:

package com.javarticles.patterns;

public class ConsumerRepository {

    public void addConsumer(String consumer) {
        System.out.println("Add consumer " + consumer);
    }

}

This is the main client of all the concrete commands. It is also responsible to set the receivers. Given a command string it knows how to return a Command object.

CommandRegistry:

package com.javarticles.patterns;

import java.util.HashMap;
import java.util.Map;

public class CommandRegistry {
    static final Map<String,Command> commandMap = new HashMap<String,Command>( );
    static {
        ConsumerRepository consumerRepository = new ConsumerRepository();
        App app = App.getInstance();
        StopCommand stopCommand = new StopCommand();
        stopCommand.setApp(app);
        stopCommand.addToMap(commandMap);
        new AddCommand(consumerRepository).addToMap(commandMap);
        new InfoCommand().addToMap(commandMap);
    }
    
    public static Command get(String cmd) throws CommandNotFoundException {
        if (!commandMap.containsKey(cmd)) {
            throw new CommandNotFoundException("Command not found " + cmd);
        }
        return commandMap.get(cmd);
    }
}

Here is a simplistic view of an application.

App:

package com.javarticles.patterns;

public class App {
    private static final App INSTANCE = new App();

    public void stop() {
        System.out.println("Stopping application...");
    }

    public static App getInstance() {
        return INSTANCE;
    }

}

Finally we invoke the command execution. This is the actual Invoker. This only knows the generic Command interface and is not aware of the actual command object.

CommandDesignPatternExample:

package com.javarticles.patterns;

import java.util.Arrays;

public class CommandDesignPatternExample {

    public static void main(String[] args) throws CommandNotFoundException, CommandException {                
        
        Command info = CommandRegistry.get("info");
        info.parseAndExec(null);
                
        Command add = CommandRegistry.get("add");
        add.parseAndExec(Arrays.asList("Consumer1", "Consumer2"));
        
        Command stop = CommandRegistry.get("stop");
        stop.parseAndExec(Arrays.asList("immediate"));
    }
}

Output:

Return Info
Add consumer Consumer1
Add consumer Consumer2
Stop [immediate]Execute stop command
Stopping application...

Download the source code

This was an example about command design pattern.

You can download the source code here: designPatternCommandPattern.zip
Share.

Comments are closed.