Command Pattern

Tldr

Instead of class C having method M, we will have MCommand class having C as class variable and call method M on this class variable in the execute method. Method logic moved from function to a class

It is just a way to **treat a function call as data**.

  • Normally, when you write service.doWork(), the code executes immediately. You can’t store it, pass it to another thread, or undo it.
  • By wrapping that call in a class (the Command), you turn that execution into an object that can be passed around.

1. The Direct Way (No Command Pattern)

This is rigid. You have no control over when or if this runs.

  1. the Receiver is the object that contains the actual business logic or the “knowledge” of how to perform the specific task.
public class TextEditor {
    public void save() { System.out.println("Saving file..."); }
}
  1. Client usage - It happens right now. You can’t undo this
TextEditor editor = new TextEditor();
editor.save();

2. The Command Way

  • Here, we turn the “Save” action into an object.
  • Now, the TextEditor doesn’t even need to know what a “Save” command is. It just executes whatever it is given.
  1. The Interface (The “Action” template)
  2. All command objects implement the same interface, which consists of one method - execute
public interface Command {
    void execute();
}
  1. The Concrete Command (Storing the action as data)
    1. We ==store the object that will do the work
    2. The action is deferred until execute() is called
public class SaveCommand implements Command {
    
    private final TextEditor editor;
    
    public SaveCommand(TextEditor editor) {
        this.editor = editor;
    }

    @Override
    public void execute() {
        editor.save(); 
    }
}
  1. The Invoker (The object that controls the execution i.e the code that is using the command)
public class Button {
    private Command command;

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

    public void click() {
        command.execute(); // The button doesn't know what it's doing; it just executes
    }
}

Why this is different (The “Power” of the Command)

Because SaveCommand is now an object, we can do things that were impossible before.

1. Storing history (Undo/Redo)

You can create a Stack<Command> history. Every time a user clicks something, you push the command onto the stack. To undo, you just pop it off and run an undo() method inside the command.

Stack<Command> undoStack = new Stack<>();
undoStack.push(new SaveCommand(editor)); 
// Now you have a history of what happened!

2. Delayed Execution

You can pass the Command object to a background thread to run it later, or even put it into a queue to avoid overloading your CPU.

// Instead of running it now:
// command.execute();

// We put it in a queue to run later:
Queue<Command> taskQueue = new LinkedList<>();
taskQueue.add(new SaveCommand(editor));

// The CPU can pick it up when it's ready:
taskQueue.poll().execute();

3. Macro Commands

You can create a command that holds other commands.

public class MacroCommand implements Command {
    private List<Command> commands;

    public void execute() {
        for (Command c : commands) c.execute(); // Run a whole sequence of actions
    }
}

The core insight

In Java, a “method” isn’t an object. You can’t put a method into a List, you can’t pass it as a variable to a function, and you can’t return it from a function (without using functional interfaces).

NOTE

The Command Pattern is the “Object-Oriented” way to package a method so that it behaves like any other variable. It allows your system to manage, log, queue, and delay execution instead of just blindly running code the moment it’s typed.

Use Command WhenSkip Command When
You need undo/redo functionalityOperations are simple and one-way
Operations should be queuedEverything executes immediately
You want to log operations for audit or replayLogging isn’t needed
You need transactional behaviorOperations are atomic and isolated