Java > Design Patterns in Java > Behavioral Patterns > Command Pattern

Command Pattern Example: Remote Control

This example demonstrates the Command Pattern using a simple remote control scenario. The remote control can turn on and off a light.

Introduction to the Command Pattern

The Command Pattern encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. It promotes loose coupling between the object issuing the request (the invoker) and the object that knows how to perform the request (the receiver).

Components of the Command Pattern

The Command Pattern typically involves these components:

  • Command: An interface declaring a method for executing a command.
  • ConcreteCommand: Implements the Command interface, holding a receiver and implementing execute() by invoking the receiver's action(s).
  • Receiver: Knows how to perform the operations associated with carrying out a request. Any class can serve as a receiver.
  • Invoker: Asks the command to carry out the request. It holds a Command object.
  • Client: Creates the Command objects and associates them with receivers.

Command Interface

This interface declares the `execute()` method that all concrete command classes will implement. The `undo()` method is added for making commands undoable.

interface Command {
    void execute();
    void undo();
}

Receiver: The Light Class

This class represents the receiver of the command. It has `turnOn()` and `turnOff()` methods to control the light.

class Light {
    private boolean isOn = false;

    public void turnOn() {
        isOn = true;
        System.out.println("Light is ON");
    }

    public void turnOff() {
        isOn = false;
        System.out.println("Light is OFF");
    }

    public boolean isOn() {
        return isOn;
    }
}

Concrete Command: TurnOnCommand

This class implements the `Command` interface and holds a reference to the `Light` object. The `execute()` method calls the `turnOn()` method of the `Light` object. The `undo()` method calls the `turnOff()` method.

class TurnOnCommand implements Command {
    private Light light;

    public TurnOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }

    @Override
    public void undo() {
        light.turnOff();
    }
}

Concrete Command: TurnOffCommand

This class implements the `Command` interface and holds a reference to the `Light` object. The `execute()` method calls the `turnOff()` method of the `Light` object. The `undo()` method calls the `turnOn()` method.

class TurnOffCommand implements Command {
    private Light light;

    public TurnOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOff();
    }

     @Override
    public void undo() {
        light.turnOn();
    }
}

Invoker: The RemoteControl Class

This class represents the invoker. It holds a `Command` object and executes the command when the `pressButton()` method is called. It also has the `pressUndo` method to undo the last command.

class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }

    public void pressUndo() {
        command.undo();
    }
}

Client: Putting it all together

This is the client code that creates the `Light`, `Command`, and `RemoteControl` objects and connects them. It then uses the `RemoteControl` to execute the commands.

public class CommandPatternExample {
    public static void main(String[] args) {
        Light light = new Light();

        Command turnOnCommand = new TurnOnCommand(light);
        Command turnOffCommand = new TurnOffCommand(light);

        RemoteControl remote = new RemoteControl();

        remote.setCommand(turnOnCommand);
        remote.pressButton(); // Light is ON

        remote.setCommand(turnOffCommand);
        remote.pressButton(); // Light is OFF

        remote.pressUndo(); //Light is ON - undo the last command
    }
}

Concepts behind the Snippet

This snippet illustrates how the Command pattern decouples the invoker (RemoteControl) from the receiver (Light). The invoker doesn't need to know the specifics of how the light is turned on or off. It only knows that it can execute a Command object. This makes the code more flexible and easier to maintain. The `undo()` method demonstrates how the command pattern can support undoable operations.

Real-Life Use Case Section

The Command pattern is used in various real-world scenarios, such as:

  • GUI applications: Menu items and buttons can be implemented as commands.
  • Transaction processing: Commands can represent transactions that need to be executed and possibly rolled back.
  • Macro recording: A sequence of user actions can be recorded as a series of commands.
  • Thread Pool: Commands can represent tasks added to a Thread Pool

Best Practices

  • Keep commands simple: Each command should represent a single, well-defined action.
  • Use a Command history: Maintain a history of executed commands to support undo/redo functionality.
  • Consider command grouping: For complex scenarios, group related commands into composite commands.

Interview Tip

Be prepared to explain the benefits of the Command pattern, such as decoupling, flexibility, and support for undoable operations. You should also be able to provide real-world examples of how the pattern is used.

When to use them

Use the Command pattern when:

  • You need to decouple the object that issues a request from the object that knows how to perform the request.
  • You want to support undoable operations.
  • You need to queue or log requests.

Memory Footprint

The Command Pattern can increase memory usage due to the creation of command objects. Each command object stores the necessary information to execute the request. In scenarios with many commands, this can become a concern. Evaluate if the decoupling benefits outweigh the memory overhead.

Alternatives

Alternatives to the Command Pattern include:

  • Direct method calls: If decoupling is not a primary concern, direct method calls may be simpler.
  • Event listeners: For event-driven systems, event listeners can be used to handle requests.
  • Strategy Pattern: If the core intent is to change the algorithm at runtime, the Strategy pattern can be applied

Pros

  • Decoupling: Reduces dependencies between objects.
  • Flexibility: Allows for easy modification and extension of commands.
  • Undo/Redo support: Facilitates implementation of undoable operations.
  • Queueing and Logging: Enables requests to be queued or logged.

Cons

  • Increased complexity: Adds more classes and interfaces to the design.
  • Memory overhead: Can lead to increased memory usage due to command objects.

FAQ

  • What is the main benefit of using the Command Pattern?

    The main benefit is decoupling the invoker from the receiver. This allows you to change the way a request is handled without modifying the invoker.
  • How does the Command Pattern support undoable operations?

    By implementing an `undo()` method in the `Command` interface, you can reverse the effects of the `execute()` method.