Command pattern is used to encapsulate a user request as an object. This tutorial explains command pattern in Java using practical examples.
What is Command Pattern?
Command pattern is a behvioral pattern which converts a request into a standalone object. This object can have member variables and methods like any other objects so it will encapsulate all the required information about the request. Let’s suppose we are building a word doc editor. we have generic button class which we can inherit from and create SaveButton, EditButton, CutButton, etc. All of these buttons will implement Button interface. This way, we may end up creating too many buttons which may be very difficult to manage.
Alternatively, we can use command pattern. In this pattern, we write generic command and have different concrete implementations like WriteCommand, CutCommand, SaveCommand, etc. The benefit with this approach is that the command is actually tied with the business logic rather than button. The main purpose behind each button action is to perform some action rather than the button name or button location. We can reuse logic of copy and paste into CutCommand or reuse business logic into UndoCommand. In order to achieve the same functionality, the Command object must also have a field for receiver. This way when this command is received, it will perform the action on the receiver object. The Command interface also defines execute() method which will be used to invoke action related to this command.
This pattern is often used for button related functionalities. For example, java.swing.Action uses this pattern. This pattern has one object per command and the contract of the command interface is mainted.
How to implement Command Pattern?
This pattern mainly consists of following components:
- Command: This is an interface or an abstract class that defines the method to execute the command.
- Concrete Commands: The subclasses of this will implement the interface and provide the concrete implementation logic for command to execute. The parameters required to execute the method on a receiving object can be declared as fields in the concrete command.
- Receiver: It is the receiver of the command. This is the object which will actually perform the task when a command object is received.
- Invoker: This is the object which holds the command object and initiates command execution by calling the
execute()method on the command object.
Practical Example - Editor Undo Feature
- Define the
Commandinterface for all commands.
1public interface Command {
2 void execute();
3 void undo();
4}
Create concrete commands by implementing the interface for all commands. In this case, I have an example of a WriteCommand which is used to write some text into a document.
1public class WriteCommand implements Command {
2 private final WordDocument document;
3 public final String text;
4 private String previousText;
5
6 public WriteCommand(WordDocument document, String text) {
7 this.document = document;
8 this.text = text;
9 this.previousText = document.getContent();
10 }
11
12 @Override
13 public void execute() {
14 System.out.println("Executing write command for text: " + text);
15 previousText = document.getContent();
16 document.write(text);
17 }
18
19 @Override
20 public void undo() {
21 System.out.println("Undoing write command for text: " + text);
22 document.setContent(document.getContent().replace(document.getContent(), previousText));
23 }
24}
Create invoker object. In this case, the Document object is the invoker. It is also the receiver of the commands.
1public interface Document {
2 void open();
3 void write(String input);
4 void save();
5 void undo();
6 void redo();
7 String getContent();
8}
1public class WordDocument implements Document {
2 private StringBuilder content;
3 private History history;
4
5 public WordDocument() {
6 content = new StringBuilder();
7 history = new History();
8 }
9
10 @Override
11 public void open() {
12 System.out.println("Document opened.");
13 }
14
15 @Override
16 public void save() {
17 System.out.println("Document saved.");
18 }
19
20 @Override
21 public void write(String content) {
22 this.history.push(new WriteCommand(this, content));
23 this.content.append(content);
24 }
25
26 @Override
27 public void undo() {
28 if (history.canUndo()) {
29 history.pop().undo();
30 }
31 }
32
33 @Override
34 public void redo() {
35 if (history.canRedo()) {
36 history.redo().execute();
37 }
38 }
39
40 public String getContent() {
41 return content.toString();
42 }
43
44 public void setContent(String content) {
45 this.content = new StringBuilder(content);
46 }
47}
This WordDocument object uses History object to store the history of the commands using a Stack data structure.
1public class History {
2 private Stack<Command> undoStack;
3 private Stack<Command> redoStack;
4
5 public History() {
6 this.undoStack = new Stack<>();
7 this.redoStack = new Stack<>();
8 }
9
10 public void push(Command command) {
11 undoStack.push(command);
12 redoStack.clear(); // Clear redo stack on new command
13 }
14
15 public Command pop() {
16 Command command = undoStack.pop();
17 redoStack.push(command);
18 return command;
19 }
20
21 public boolean canUndo() {
22 return !undoStack.isEmpty();
23 }
24
25 public boolean canRedo() {
26 return !redoStack.isEmpty();
27 }
28
29 public Command redo() {
30 return redoStack.pop();
31 }
32}
The client can simply invoke commands using simple interface. All the details to the receiver are passed in the form of an object.
1public class ClientMain {
2 public static void main(String[] args) {
3 Document wordDoc = new WordDocument();
4 wordDoc.open();
5 wordDoc.write("Hello World!!! ");
6 wordDoc.write("Hi there! ");
7 System.out.println("Content: " + wordDoc.getContent());
8 wordDoc.undo();
9 System.out.println("Content: " + wordDoc.getContent());
10 wordDoc.write("How are you?");
11 System.out.println("Content: " + wordDoc.getContent());
12 wordDoc.undo();
13 System.out.println("Content: " + wordDoc.getContent());
14 }
15}
Advantages:
- It decouples sender and receiver and provides more flexibility.
- The same command can be applied of different receivers. Adding different kinds of parameters do not change the invoker object which initiates the command.
Disadvantages:
- It can sometimes be difficult to understand when this pattern can be used.
- It can be little difficult to understand with lots of commands.
Comparison with Strategy Pattern
| Command Pattern | Strategy Pattern |
|---|---|
| It’s structured around an object per command | This is object per request with focus on strategy |
| The command object contains what we need to do | These objects contain how we want to implement algorithm |
| It encapsulates entire action to be performed | It encapsulates the algorithm to be applied. |
Summary
- This is one of the most popular patterns to abstract requests as set of commands.
- Command pattern encapsulates each request as an object and decouples sender and receiver.


Comments