The Command Pattern is one of the Behavioral Design Patterns that focuses on encapsulating a request as an object. This pattern allows you to parameterize clients with queues, requests, and operations, making it easier to manage and execute commands dynamically.
In simpler terms, the Command Pattern transforms a request into a stand-alone object that contains all information about the request. This includes the method name, the object that owns the method, and values for the method’s parameters. The transformation allows you to parameterize methods with different requests, delay or queue a request's execution, and support undoable operations.
The Command Pattern consists of several key components:
execute method by invoking a specific action on a receiver object.Here's a simple diagram to illustrate these components:
+-------------------+
| Invoker |
|-------------------|
| - command: Command |
| + setCommand() |
| + executeCommand() |
+---------+----------+
|
v
+-------------------+
| ConcreteCommand |
|-------------------|
| - receiver: Receiver |
| + execute() |
+---------+----------+
|
v
+-------------------+
| Receiver |
|-------------------|
| + action() |
+-------------------+
Let's start with a basic implementation of the Command Pattern. We'll create a simple remote control system where different commands can be executed.
The receiver is an object that knows how to perform the operations associated with carrying out a request.
1class Light {2turnOn() {3console.log('Light is on');4}56turnOff() {7console.log('Light is off');8}9}
The command interface declares an execute method.
1class Command {2execute() {3throw new Error("This method must be overridden by subclasses");4}5}
Concrete commands implement the execute method by invoking a specific action on a receiver object.
1class LightOnCommand extends Command {2constructor(light) {3super();4this.light = light;5}67execute() {8this.light.turnOn();9}10}1112class LightOffCommand extends Command {13constructor(light) {14super();15this.light = light;16}1718execute() {19this.light.turnOff();20}21}
The invoker is an object that asks the command to carry out the request.
1class RemoteControl {2constructor(command) {3this.command = command;4}56setCommand(command) {7this.command = command;8}910executeCommand() {11this.command.execute();12}13}
Now, let's use the command pattern to control a light.
1const light = new Light();2const lightOnCommand = new LightOnCommand(light);3const lightOffCommand = new LightOffCommand(light);45const remoteControl = new RemoteControl();67remoteControl.setCommand(lightOnCommand);8remoteControl.executeCommand(); // Output: Light is on910remoteControl.setCommand(lightOffCommand);11remoteControl.executeCommand(); // Output: Light is off
A macro command is a command that contains a list of commands. It can execute all the commands in its list.
1class MacroCommand extends Command {2constructor(commands) {3super();4this.commands = commands;5}67execute() {8this.commands.forEach(command => command.execute());9}10}
Let's create a macro command to turn on the light and then turn it off.
1const macroCommand = new MacroCommand([lightOnCommand, lightOffCommand]);23remoteControl.setCommand(macroCommand);4remoteControl.executeCommand();5// Output:6// Light is on7// Light is off
In the next section, we'll explore the Interpreter Pattern, which provides a way to evaluate language grammar or expression. This pattern is particularly useful for parsing and interpreting complex expressions.
Stay tuned!