Skip to Content
Course content

155: The Command Pattern

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've likely reached a point in your project where you have a class that's starting to look like a "God Object"—a piece of code that knows too much and does too much. Usually, this happens when you're tying your user interface (like a button or a menu) directly to the business logic. That's where the Command Pattern comes in. It's basically a way to turn a request into a stand-alone object.

Why can't I just call the method directly on the object?

Sure, you could. If your Button class calls light.turnOn() directly, it works. But now your Button is hard-wired to the Light class. What happens when you want that same button to open the garage door? Or what if you want to group three different actions—turn off the lights, lock the doors, and arm the alarm—into one "Leave House" button?

By using the Command Pattern, the Button doesn't know what it's actually doing. It just knows it has a Command object and it needs to call execute() on it. I've seen this save countless hours of refactoring in large-scale systems because it completely decouples the object that triggers the action from the object that actually performs the logic.

How do I actually structure this in Java?

You need four things: a Command interface, a Concrete Command, a Receiver (the thing actually doing the work), and an Invoker (the thing triggering the command). Let's look at a smart home example.

// The Command Interface
interface Command {
    void execute();
    void undo();
}

// The Receiver: This is where the actual logic lives
class Light {
    public void on() { System.out.println("Light is ON"); }
    public void off() { System.out.println("Light is OFF"); }
}

// Concrete Command: It wraps the receiver
class LightOnCommand implements Command {
    private Light light;

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

    public void execute() {
        light.on();
    }

    public void undo() {
        light.off();
    }
}

// The Invoker: It doesn't know what the command does, just how to trigger it
class RemoteControl {
    private Command slot;

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

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

Notice that the RemoteControl doesn't have a single reference to the Light class. It only knows about the Command interface. This is the "magic" of the pattern.

Is this how "Undo" functionality is actually built?

Exactly. This is the most common real-world use case for the Command Pattern. If you just call methods directly, you have to manually track the previous state of every single object to reverse an action. That's a nightmare.

With the Command Pattern, the ConcreteCommand object stores the state needed to reverse the action. In the example above, LightOnCommand knows that the opposite of on() is off(). If you're building a text editor, a PasteCommand would store the text that was overwritten so it can put it back during undo().

To make this professional, I usually recommend keeping a Stack<Command> in your Invoker. Every time you call execute(), you push that command onto the stack. When the user hits Ctrl+Z, you pop the last command and call its undo() method. It's clean, predictable, and easy to test.




📋 Practical Task

Exercise: Implementing a Macro-Enabled Smart Home Controller

You are building a home automation system. You already have the Command interface and Light / Fan receiver classes. Your goal is to implement a MacroCommand.

A MacroCommand is a command that contains a List<Command>. When its execute() method is called, it should loop through all the commands in its list and execute them in order. This allows the user to trigger multiple actions (e.g., "Movie Mode": dim lights, turn on projector, close blinds) with a single button press.

Requirements:

  • Create a class MacroCommand that implements the Command interface.
  • Add a constructor that accepts a List<Command>.
  • Implement execute() to trigger all commands in the list.
  • Implement undo() to trigger the undo operations of the commands in the list in reverse order (this is crucial for maintaining system state).
  • In your main method, create a "Party Mode" macro that turns on multiple lights and a music system, then execute it and undo it.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.