Command Design Pattern in JavaScript

1. Definition

The Command Design Pattern encapsulates a request as an object, allowing users to parameterize objects with different requests, queue requests, log the operations, and support undoable operations.

2. Problem Statement

Consider you are designing a remote control for various household devices like lights, fans, and TVs. Implementing individual methods for each action on every device would become messy, especially if you need to introduce undo functionality or want to log every action. How can you organize your code in a way that is scalable and maintains a separation of concerns?

3. Solution

Encapsulate each operation inside a command object that has a specific action and the receiver associated with the action. This allows decoupling the objects that issue commands from the objects that perform the action.

4. Real-World Use Cases

1. GUI buttons and menu items in software. For instance, the Copy, Paste, and Undo functionalities in word processors.

2. Macro recording where sequences of commands can be saved and replayed.

3. Operations queue, where commands are queued up and executed at specific times or under certain conditions.

5. Implementation Steps

1. Define a command interface with an execute method.

2. Create one or more derived classes that encapsulate a receiver and an action.

3. Instantiate command objects and pass to invoker classes.

4. The invoker invokes the command.

6. Implementation in JavaScript

// Command interface
class Command {
    execute() {}
}
// Concrete command
class LightOnCommand extends Command {
    constructor(light) {
        super();
        this.light = light;  // Receiver
    }
    execute() {
        this.light.turnOn();
    }
}
class Light {
    turnOn() {
        console.log("Light is ON");
    }
}
// Invoker
class RemoteControl {
    setCommand(command) {
        this.command = command;
    }
    pressButton() {
        this.command.execute();
    }
}
// Client Code
const light = new Light();
const lightOnCommand = new LightOnCommand(light);
const remote = new RemoteControl();
remote.setCommand(lightOnCommand);
remote.pressButton();

Output:

Light is ON

Explanation:

1. The Command class is the command interface that has an execute method.

2. LightOnCommand is a concrete command that encapsulates the receiver (Light) and invokes its method.

3. RemoteControl is the invoker. It's provided with a command object that can be executed.

4. In the client code, we instantiate a Light object (receiver), wrap a command around it (LightOnCommand), and associate the command with the invoker (RemoteControl).

5. When the pressButton method of the invoker is called, the command's execute method gets triggered, turning on the light.

6. The system is organized in such a way that adding new commands or receivers won't affect existing code.

7. When to use?

The Command Pattern is useful when:

1. You need to decouple the sender from the receiver by allowing different senders to reuse the same commands.

2. You want support for undo. Commands can keep state for reversing its effects.

3. Requests need to be handled at different times or in different orders.

4. The invoker should be decoupled from the system's functionality.


Comments