The command pattern involves creating an object that encapsulates all the information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.

Problem

The example that we will be using over here is a simple text editor. The text editor has a Document class that contains a String that represents the text in the document. The Document class also has a open() and save() method that opens and saves the document respectively.

Each method that we want to implement, it will do something to the text that is contained within the element.

Using naive methods, I will have to create buttons for the function that I want to implement. For example, if I want to implement a bold function, I will have to create a button that will call the bold() method. This will be a problem if I want to add more functions to the text editor. I will have to create more buttons for the new functions that I want to implement.

This will create many different types of buttons that will be used for different functions. This will also create a lot of code that will be used to create the buttons.

So how do we solve this?

The solution to this problem is to create a Command interface that will contain the execute() method. This method will be used to execute the command that is passed into the method.

Each of the command interfaces will have a method that allows the command to be executed. In this case, the execute() method will be used to execute the command.

public interface Command {
    void execute(TextEditor textEditor);
}

An example of this will be the bold command.

public class BoldCommand implements Command {
    @Override
    public void execute(TextEditor textEditor) {
        textEditor.selectedText = "<b>" + textEditor.selectedText + "</b>";
    }
}

In this case there is only 1 button implementation required.

public class Button {
    private Command command;

    public Button(Command command) {
        this.command = command;
    }

    public void click(TextEditor textEditor) {
        command.execute();
    }
}

Advantages and Disadvantages

Advantages

  • It decouples the object that invokes the operation from the one that knows how to perform it.
  • It may easier to implement undo/redo.
    • Each command just has to implement an undo() method.
    • The commands used are kept as a stack and undoing is just looking at the top of the stack and running the undo command for the command
  • Commands can be composed to form more complicated commands without reimplementing them.
    • For example, a BoldCommand can be composed with an ItalicCommand to form a BoldItalicCommand.
  • Commands can be differed and executed at a later time.
    • For example, a BoldCommand can be deferred and executed at a later time when printing the document instead of running immediately.

This follows the principles of Single Responsibility as well as the Open / Closed principle

Disadvantages

  • It can make the code more complicated since you will have to create a new class for every command.
  • Over-engineering when there are only a few commands

How to effectively apply this pattern?

  • Use this pattern when you want to issue requests to objects without knowing anything about the operation being requested or the receiver of the request.
  • Use this pattern when you want to be able to implement reversible operations.
  • Use this pattern when you want to be able to implement deferred execution of operations.

Applications in real life

  • Text editors
  • IDEs
  • GUIs
  • Game engines

Analogies

  • The command pattern is similar to a recipe that is handed over to a chef.
  • The recipe is the command and the chef is the receiver.
  • The chef does not know beforehand what ihe/she is going to cook but it knows how to cook it.

Conclusions

The command pattern is a very useful pattern that can be used to decouple the object that invokes the operation from the one that knows how to perform it. It can also be used to implement undo/redo and deferred execution of operations.

  1. The refactoring Guru