Introduction

The Command pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation allows for parameterizing methods with different requests, delaying or queuing a request's execution, and supporting undoable operations.

Key Concepts

  • Command: An interface or abstract class that declares a method for executing a command.
  • ConcreteCommand: A class that implements the Command interface and defines the binding between a Receiver object and an action.
  • Receiver: The object that performs the actual work when the command's execute method is called.
  • Invoker: The object that knows how to execute a command but doesn't know how the command has been implemented.
  • Client: The object that creates a ConcreteCommand and sets its receiver.

Structure

Here's a UML diagram of the Command pattern:

Client --> Invoker --> Command --> ConcreteCommand --> Receiver

Components

  1. Command Interface

    public interface Command {
        void execute();
    }
    
  2. ConcreteCommand

    public class LightOnCommand implements Command {
        private Light light;
    
        public LightOnCommand(Light light) {
            this.light = light;
        }
    
        @Override
        public void execute() {
            light.on();
        }
    }
    
  3. Receiver

    public class Light {
        public void on() {
            System.out.println("The light is on");
        }
    
        public void off() {
            System.out.println("The light is off");
        }
    }
    
  4. Invoker

    public class RemoteControl {
        private Command command;
    
        public void setCommand(Command command) {
            this.command = command;
        }
    
        public void pressButton() {
            command.execute();
        }
    }
    
  5. Client

    public class Client {
        public static void main(String[] args) {
            Light light = new Light();
            Command lightOn = new LightOnCommand(light);
    
            RemoteControl remote = new RemoteControl();
            remote.setCommand(lightOn);
            remote.pressButton();
        }
    }
    

Example Explained

  1. Command Interface: The Command interface declares an execute method.
  2. ConcreteCommand: The LightOnCommand class implements the Command interface and binds the Light receiver to the on action.
  3. Receiver: The Light class contains the actual business logic to turn the light on and off.
  4. Invoker: The RemoteControl class holds a command and triggers its execution.
  5. Client: The Client class creates the receiver, binds it to a command, and sets the command in the invoker.

Practical Exercise

Task

Create a command pattern implementation for a simple text editor that supports the following operations:

  • Write text
  • Undo the last operation

Solution

  1. Command Interface

    public interface Command {
        void execute();
        void undo();
    }
    
  2. ConcreteCommand

    public class WriteCommand implements Command {
        private String text;
        private StringBuilder document;
    
        public WriteCommand(StringBuilder document, String text) {
            this.document = document;
            this.text = text;
        }
    
        @Override
        public void execute() {
            document.append(text);
        }
    
        @Override
        public void undo() {
            document.delete(document.length() - text.length(), document.length());
        }
    }
    
  3. Receiver

    public class Document {
        private StringBuilder content = new StringBuilder();
    
        public void write(String text) {
            content.append(text);
        }
    
        public void undoWrite(String text) {
            content.delete(content.length() - text.length(), content.length());
        }
    
        public String getContent() {
            return content.toString();
        }
    }
    
  4. Invoker

    public class TextEditor {
        private Command command;
    
        public void setCommand(Command command) {
            this.command = command;
        }
    
        public void type() {
            command.execute();
        }
    
        public void undo() {
            command.undo();
        }
    }
    
  5. Client

    public class Client {
        public static void main(String[] args) {
            Document document = new Document();
            Command writeHello = new WriteCommand(document.getContent(), "Hello ");
            Command writeWorld = new WriteCommand(document.getContent(), "World!");
    
            TextEditor editor = new TextEditor();
            editor.setCommand(writeHello);
            editor.type();
            System.out.println(document.getContent());
    
            editor.setCommand(writeWorld);
            editor.type();
            System.out.println(document.getContent());
    
            editor.undo();
            System.out.println(document.getContent());
        }
    }
    

Explanation

  • Command Interface: The Command interface now includes an undo method.
  • ConcreteCommand: The WriteCommand class implements the Command interface and provides the logic for writing and undoing text.
  • Receiver: The Document class contains the actual text and methods to modify it.
  • Invoker: The TextEditor class holds a command and triggers its execution or undo.
  • Client: The Client class creates the receiver, binds it to commands, and sets the commands in the invoker.

Common Mistakes and Tips

  • Mistake: Forgetting to implement the undo method in the ConcreteCommand.
    • Tip: Always ensure that both execute and undo methods are implemented to maintain consistency.
  • Mistake: Directly modifying the receiver in the invoker.
    • Tip: The invoker should only call the command's execute or undo methods, not modify the receiver directly.

Conclusion

The Command pattern is a powerful tool for decoupling the sender and receiver of a request, allowing for flexible and reusable code. By encapsulating requests as objects, you can easily parameterize methods, queue requests, and implement undoable operations. This pattern is particularly useful in scenarios like GUI buttons, menu actions, and text editors.

In the next topic, we will explore the Interpreter pattern, which is another behavioral design pattern that deals with defining a grammar for a language and interpreting sentences in that language.

© Copyright 2024. All rights reserved