Other Tutorials

Command Design Pattern In Java

Introduction:

In this tutorial, we’ll learn about the command pattern which is an important behavioral design pattern. It has some important applications like implementing undo/redo functionality in text editors.

In the command design pattern, there’s a command object that sits between the sender and the receiver objects. The sender object can create a command object. The command object then calls the exposed method in the receiver. And so, the sender object doesn’t need to know about the receiver and its exposed methods.

We also have another object known as invoker. An invoker is an object responsible for invoking the appropriate command object to complete a task. We can also use a command manager which keeps track of commands, invokes and manipulates them.

Why Command Design Pattern?

There are some popular use-cases of the command pattern:

  • Storing and Scheduling requests: We can represent our requests as command objects which can then be stored onto lists, manipulated, queued or completed at a specific time via event trigger. For example, alarm ringing functionality can be achieved by representing it as a command object and executing it on an event trigger
  • Supports Done/Undone: The command pattern enables us to do or undo an operation performed by the command

As each command object supports do/undo operation, we can extend this functionality to design do/undo operation for a text editor. The idea is to have two lists of command objects – a history and a redo list:

  • The history list holds all commands that have been executed so far
  • A redo list, on the other hand, stores commands that we have undone
  • For each command request, a command object is created, executed and then added to the history list
  • On an undo request, we’ll check and call the undo operation on the most recent command on the history list and then put that command on the redo list
  • For a redo operation, we’ll take the most recent command on the redo list, the one we have undone recently, and then execute it and move it onto the history list again

Sounds simple right!

UML Representation:

We can represent the command design pattern as:

Command Design Pattern

Where we have,

  • Command: an interface or an abstract class defining operations for the command objects.
  • ConcreteCommand: these are the concrete classes that hold the actual implementation for a specific command
  • Receiver: command class invokes a receiver to perform the requested operation
  • Invoker: a class that’s exposed to the client. It’s responsible to invoke the appropriate command

Moreover, each command class usually provides the implementation of these methods:

  • execute(): defines the work that’s supposed to be done
  • unexecute(): this is the method responsible for undoing the operation
  • isReversible(): this method should return true if the command can be undone, false otherwise

Example Implementation:

Suppose we have to implement the cut-copy-paste functionality for a text editor.

So, we start by defining our Command interface:

public interface Command {

    void execute();
    void unexecute();

    default boolean isReversible() {
        return true;
    }
}

Also, let’s assume we have a Document class supporting text insertion and deletion:

//class which will be our Receiver
public class Document {

    public void insert(String str, int position) {
        ...
    }

    public String delete(int position, int noOfChars) {
        ...
    }

    public void copy(int position, int noOfChars) {
        ...
    }
}

Writing Concrete Commands:

Now, we’ll define our CutCommand class:

public class CutCommand implements Command {

    private Document doc;
    private String text;
    private int startPosition;
    private int noOfChars;

    //suitable constructor

    public void execute() {
        this.text = this.doc.delete(startPosition, noOfChars);
    }

    public void unexecute() {
        this.doc.insert(text, startPosition);
    }
}

Let’s also define the other two command classes as well:

public class CopyCommand implements Command {

    private Document doc;
    private int startPosition;
    private int length;

    //suitable constructor

    public void execute() {
        this.doc.copy(startPosition, length);
    }

    public void unexecute() {
        System.out.println("Uncopy operation is blocked");
    }

    public boolean isReversible() { return false; }
}

public class PasteCommand implements Command {

    private Document doc;
    private String text;
    private int startPosition;

    //suitable constructor

    public void execute() {
        this.doc.insert(text, startPosition);
    }

    public void unexecute() {
        this.doc.delete(startPosition, text.length());
    }
}

As we know, uncopy is not a valid operation, we have returned false in our isReversible() method of the CopyCommand class.

Implementing Invoker:

Finally, we can write an invoker class:

public class DocumentInvoker {

    private Document document;
    private CommandManager commandManager;

    public DocumentInvoker(Document document) {
        this.document = document;
        commandManager = CommandManage.getInstance();
    }

    public void cut(int position, int length) {
        Command cutCommand = new CutCommand(document, position, length);
        commandManager.invoke(cutCommand);
    }

    public void copy(int position, int length) {
        Command copyCommand = new CopyCommand(document, position, length);
        commandManager.invoke(copyCommand);
    }

   public void paste(String text, int position) {
        Command pasteCommand = new PasteCommand(document, text, position);
        commandManager.invoke(pasteCommand);
    }

}

Here, the CommandManager is the class that manages the history and the redo lists. The invoker instantiates the command object with the information it needs and then calls the command manager to finally perform the operation.

Conclusion:

In this tutorial, we learned how to implement the Command Design Pattern in Java.

It promotes loose coupling as the sender need not know anything about the receiver and can simply invoke operations.

 

Be the First to comment.

Leave a Comment

Your email address will not be published. Required fields are marked *