Command Design Pattern
Structural: Command Pattern
What is the Command Design Pattern?
View Answer:
The command pattern involves four participants: command, receiver, invoker, and client.
- Command – A command object knows the receiver and calls the receiver's method. The command contains values for the receiver method's arguments.
- Client – The client is responsible for creating the command object and passing it to the invoker.
- Invoker – The invoker receives the command object from the client and is exclusively responsible for calling (or invoking) a command.
- Receiver – The receiver receives the command and searches for a method to invoke based on the received command.
Here's an example of how to implement the Command pattern in modern JavaScript.
// The Command class
class Command {
constructor(subject) {
this.subject = subject;
this.commandsExecuted = [];
}
execute(command) {
this.commandsExecuted.push(command);
return this.subject[command]();
}
}
// The subject class
class BankAccount {
constructor() {
this.balance = 0;
}
deposit() {
this.balance += 10;
console.log(`Balance is: ${this.balance}`);
}
withdraw() {
if(this.balance >= 10) {
this.balance -= 10;
}
console.log(`Balance is: ${this.balance}`);
}
}
// Use the Command and the BankAccount classes
const bankAccount = new BankAccount();
const bankAccountCommand = new Command(bankAccount);
bankAccountCommand.execute('deposit');
bankAccountCommand.execute('withdraw');
console.log(`Commands Executed: ${bankAccountCommand.commandsExecuted}`);
In this example, Command
is the command class. It has a method execute
that, when called with a command name, executes that command on the subject (an instance of BankAccount
). It also keeps track of the commands that have been executed. BankAccount
is the subject class, and it has two methods, deposit
and withdraw
, that can be executed through the command class.
In the usage section, we create instances of BankAccount
and Command
, execute some commands on the bank account through the command instance, and print out the commands that have been executed.
To which pattern family does the Command pattern belong?
View Answer:
When should you utilize the JavaScript command pattern?
View Answer:
- If you want to queue and execute requests at different times.
- If you're going to perform operations such as reset or undo.
- If you're going to keep a history of requests made.
What are the benefits of using the Command pattern?
View Answer:
- Singular Responsibility Principle Classes that invoke operations get separated from classes that perform these actions.
- The Open/Closed Principle - You can add new commands to the app without affecting the client code that already exists.
- You can use the undo/redo feature.
- You can use deferred operation execution.
- You can combine a series of simple commands to create a more sophisticated one.
What are some of the Command pattern's drawbacks?
View Answer:
Are there any alternatives to using the Command pattern?
View Answer:
What are some typical uses of the Command Pattern?
View Answer:
Here's a practical example in modern JavaScript, which illustrates undo operation.
// Command class
class Command {
constructor(execute, undo, value) {
this.execute = execute;
this.undo = undo;
this.value = value;
}
}
// Calculator class
class Calculator {
constructor() {
this.currentValue = 0;
this.commands = [];
}
executeCommand(command) {
this.currentValue = command.execute(this.currentValue, command.value);
this.commands.push(command);
}
undoCommand() {
const command = this.commands.pop();
this.currentValue = command.undo(this.currentValue, command.value);
}
getCurrentValue() {
return this.currentValue;
}
}
// Calculator operations
function add(value, amount) {
return value + amount;
}
function subtract(value, amount) {
return value - amount;
}
// Usage
const calculator = new Calculator();
// Add 10
const addCommand = new Command(add, subtract, 10);
calculator.executeCommand(addCommand);
console.log(calculator.getCurrentValue()); // Output: 10
// Subtract 5
const subtractCommand = new Command(subtract, add, 5);
calculator.executeCommand(subtractCommand);
console.log(calculator.getCurrentValue()); // Output: 5
// Undo last command (Subtract 5)
calculator.undoCommand();
console.log(calculator.getCurrentValue()); // Output: 10
In this example, the Calculator class uses the Command pattern to handle arithmetic operations. Each operation (add, subtract) is encapsulated in a Command object, which has an execute
function to carry out the operation and an undo
function to reverse it. The Calculator maintains a history of executed commands and provides an undoCommand
method to undo the most recently executed command. This pattern provides a flexible and powerful way to organize and manipulate operations.
What are the main components of the Command Pattern?
View Answer:
The main components of the Command Pattern are:
- Command: This is the interface for executing operations.
- ConcreteCommand: This class extends the Command interface and implements the execute method.
- Client: This class creates and executes the command.
- Invoker: This class asks the command to carry out the request.
- Receiver: This is the class that performs the actual work when the execute method in ConcreteCommand is called.
Here's an example of how to implement these components in modern JavaScript:
// Receiver
class Light {
turnOn() {
console.log("The light is on");
}
turnOff() {
console.log("The light is off");
}
}
// Command
class Command {
constructor(light) {
this.light = light;
}
execute() {}
}
// ConcreteCommand
class OnCommand extends Command {
constructor(light) {
super(light);
}
execute() {
this.light.turnOn();
}
}
class OffCommand extends Command {
constructor(light) {
super(light);
}
execute() {
this.light.turnOff();
}
}
// Invoker
class Switch {
constructor(onCommand, offCommand) {
this.onCommand = onCommand;
this.offCommand = offCommand;
}
flipUp() {
this.onCommand.execute();
}
flipDown() {
this.offCommand.execute();
}
}
// Client
let light = new Light();
let onCommand = new OnCommand(light);
let offCommand = new OffCommand(light);
let switchButton = new Switch(onCommand, offCommand);
switchButton.flipUp(); // Output: The light is on
switchButton.flipDown(); // Output: The light is off
In this example:
Light
is the Receiver class that performs the actual operations (turning the light on or off).Command
is the Command interface that declares theexecute
method.OnCommand
andOffCommand
are the ConcreteCommand classes that implement theexecute
method.Switch
is the Invoker class that calls theexecute
method on a Command object.
The client, represented in the last section of the code, creates instances of the Receiver, ConcreteCommand, and Invoker classes, and then invokes the commands through the Invoker.
What's the role of the 'Command' component in the Command pattern?
View Answer:
Can you explain the 'Receiver' in the Command Pattern?
View Answer:
How does the 'Invoker' function in the Command pattern?
View Answer:
What is the role of the 'Client' in the Command Pattern?
View Answer:
How does Command Pattern support Undo operations?
View Answer:
Here's a simple example of how to support undo operations using the Command Pattern in JavaScript:
class Command {
constructor(execute, undo, value) {
this.execute = execute;
this.undo = undo;
this.value = value;
}
}
class Calculator {
constructor() {
this.currentValue = 0;
this.commands = [];
}
executeCommand(command) {
this.currentValue = command.execute(this.currentValue, command.value);
this.commands.push(command);
}
undo() {
const command = this.commands.pop();
this.currentValue = command.undo(this.currentValue, command.value);
}
getValue() {
return this.currentValue;
}
}
function add(value, amount) {
return value + amount;
}
function subtract(value, amount) {
return value - amount;
}
const calculator = new Calculator();
// execute add command
const addCommand = new Command(add, subtract, 10);
calculator.executeCommand(addCommand);
console.log(calculator.getValue()); // Output: 10
// undo add command
calculator.undo();
console.log(calculator.getValue()); // Output: 0
In this example, the Command class encapsulates an action (add) and its undo action (subtract) along with the value associated with the action. The Calculator class keeps track of the commands that are executed. When the undo method is called on the Calculator, it retrieves the most recent command and calls its undo action.