Command

Command Pattern

Introduction

The Command pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you parameterize methods with different requests, delay or queue a request’s execution, and support undoable operations.

Purpose

  • To encapsulate a request as an object, thereby allowing users to parameterize clients with queues, requests, and operations.
  • To support undoable operations.
  • To provide a means to decouple the sender of a request from its receiver.

Use Cases

  • When you need to parameterize objects according to an action to perform.
  • When you need to queue, log, or undo operations.
  • When you want to support rollback operations or transactional behavior.

Advantages/Disadvantages

Advantages:

  • Decouples the classes that invoke operations from the classes that perform these operations.
  • Provides flexibility in choosing when and how to execute operations.
  • Can assemble a set of commands into a complex operation.

Disadvantages:

  • Can lead to more complex code due to the addition of new classes.
  • Overhead of a command object and extra indirection of executing a command.

Implementation in JavaScript

javascript
	class Command {
	  execute() {}
	}
	
	class ConcreteCommand extends Command {
	  constructor(receiver, state) {
	    super();
	    this.receiver = receiver;
	    this.state = state;
	  }
	
	  execute() {
	    console.log(`Executing command with state: ${this.state}`);
	    this.receiver.action(this.state);
	  }
	}
	
	class Receiver {
	  action(state) {
	    console.log(`Receiver acting on: ${state}`);
	  }
	}
	
	// Usage
	const receiver = new Receiver();
	const command = new ConcreteCommand(receiver, 'some state');
	command.execute();

Practical Example

In a backend scenario, you could have a task scheduling system where tasks are encapsulated as commands, making it easy to manage scheduling, logging, and executing these tasks.

javascript
	class Task {
	  constructor(name, payload) {
	    this.name = name;
	    this.payload = payload;
	  }
	
	  perform() {
	    console.log(`Performing task: ${this.name} with payload: ${this.payload}`);
	  }
	}
	
	class TaskCommand {
	  constructor(task) {
	    this.task = task;
	  }
	
	  execute() {
	    this.task.perform();
	  }
	}
	
	// Usage
	const task1 = new Task('DatabaseBackup', 'db1');
	const taskCommand1 = new TaskCommand(task1);
	taskCommand1.execute();
	
	const task2 = new Task('SendEmail', 'user@example.com');
	const taskCommand2 = new TaskCommand(task2);
	taskCommand2.execute();

In this example, each Task instance represents a specific task. TaskCommand encapsulates a task and its execution logic. This approach allows the scheduling and execution of tasks to be managed independently of the task’s specific details.