Command Pattern
Command Pattern: Turning Requests into Objects
Section titled “Command Pattern: Turning Requests into Objects”Now let’s dive into the Command Pattern - one of the most versatile behavioral design patterns that encapsulates a request as an object, letting you parameterize clients with different requests, queue operations, log them, and support undoable operations.
Why Command Pattern?
Section titled “Why Command Pattern?”Imagine you’re using a text editor. You type some text, then click Undo - the text disappears. Click Redo - it comes back. How does this work? The Command Pattern! Each action (typing, deleting, formatting) is wrapped in a command object that knows how to execute itself AND how to undo itself.
The Command Pattern turns requests into stand-alone objects containing all information about the request. This transformation lets you pass requests as method arguments, delay or queue a request’s execution, and support undoable operations.
What’s the Use of Command Pattern?
Section titled “What’s the Use of Command Pattern?”The Command Pattern is useful when:
- You need undo/redo functionality - Commands can be reversed
- You want to queue operations - Execute commands later or in sequence
- You need to log operations - Commands can be serialized and logged
- You want to decouple sender from receiver - Invoker doesn’t know about receiver
- You need transactional behavior - Rollback if something fails
What Happens If We Don’t Use Command Pattern?
Section titled “What Happens If We Don’t Use Command Pattern?”Without the Command Pattern, you might:
- Tight coupling - Invoker directly calls receiver methods
- No undo support - Have to manually track all changes
- Hard to queue operations - Operations execute immediately
- No operation logging - Can’t track what was done
- Scattered code - Same operation code repeated everywhere
Simple Example: The Remote Control
Section titled “Simple Example: The Remote Control”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Interaction Flow
Section titled “Interaction Flow”Here’s how the Command Pattern works in practice - showing how commands are executed and undone:
The Problem
Section titled “The Problem”You’re building a smart home system with a remote control. The remote needs to control different devices (lights, fans, TVs). Without Command Pattern:
# ❌ Without Command Pattern - Tight coupling, no undo!
class Light: def __init__(self, location: str): self.location = location self.is_on = False
def turn_on(self): self.is_on = True print(f"💡 {self.location} light is ON")
def turn_off(self): self.is_on = False print(f"💡 {self.location} light is OFF")
class Fan: def __init__(self, location: str): self.location = location self.speed = 0
def set_speed(self, speed: int): self.speed = speed print(f"🌀 {self.location} fan speed: {speed}")
class RemoteControl: def __init__(self): # Problem: Remote knows about ALL device types! self.light = None self.fan = None
def press_light_on(self): if self.light: self.light.turn_on()
def press_light_off(self): if self.light: self.light.turn_off()
def press_fan_high(self): if self.fan: self.fan.set_speed(3)
def press_fan_off(self): if self.fan: self.fan.set_speed(0)
# Problems: # - Remote tightly coupled to device types # - Need to modify Remote for each new device # - No undo functionality # - No way to queue commands
# Usageremote = RemoteControl()remote.light = Light("Living Room")remote.press_light_on()# How do we undo? No easy way!// ❌ Without Command Pattern - Tight coupling, no undo!
class Light { private String location; private boolean isOn = false;
public Light(String location) { this.location = location; }
public void turnOn() { isOn = true; System.out.println("💡 " + location + " light is ON"); }
public void turnOff() { isOn = false; System.out.println("💡 " + location + " light is OFF"); }}
class Fan { private String location; private int speed = 0;
public Fan(String location) { this.location = location; }
public void setSpeed(int speed) { this.speed = speed; System.out.println("🌀 " + location + " fan speed: " + speed); }}
class RemoteControl { // Problem: Remote knows about ALL device types! private Light light = null; private Fan fan = null;
public void setLight(Light light) { this.light = light; }
public void setFan(Fan fan) { this.fan = fan; }
public void pressLightOn() { if (light != null) { light.turnOn(); } }
public void pressLightOff() { if (light != null) { light.turnOff(); } }
public void pressFanHigh() { if (fan != null) { fan.setSpeed(3); } }
public void pressFanOff() { if (fan != null) { fan.setSpeed(0); } }
// Problems: // - Remote tightly coupled to device types // - Need to modify Remote for each new device // - No undo functionality // - No way to queue commands}
// Usagepublic class Main { public static void main(String[] args) { RemoteControl remote = new RemoteControl(); remote.setLight(new Light("Living Room")); remote.pressLightOn(); // How do we undo? No easy way! }}// ❌ Without Command Pattern - Tight coupling, no undo!
class Light { private isOn: boolean = false;
constructor(private location: string) {}
turnOn(): void { this.isOn = true; console.log(`💡 ${this.location} light is ON`); }
turnOff(): void { this.isOn = false; console.log(`💡 ${this.location} light is OFF`); }}
class Fan { private speed: number = 0;
constructor(private location: string) {}
setSpeed(speed: number): void { this.speed = speed; console.log(`🌀 ${this.location} fan speed: ${speed}`); }}
class RemoteControl { // Problem: Remote knows about ALL device types! private light: Light | null = null; private fan: Fan | null = null;
setLight(light: Light): void { this.light = light; }
setFan(fan: Fan): void { this.fan = fan; }
pressLightOn(): void { if (this.light) { this.light.turnOn(); } }
pressLightOff(): void { if (this.light) { this.light.turnOff(); } }
pressFanHigh(): void { if (this.fan) { this.fan.setSpeed(3); } }
pressFanOff(): void { if (this.fan) { this.fan.setSpeed(0); } }
// Problems: // - Remote tightly coupled to device types // - Need to modify Remote for each new device // - No undo functionality // - No way to queue commands}
// Usageconst remote = new RemoteControl();remote.setLight(new Light("Living Room"));remote.pressLightOn();// How do we undo? No easy way!// ❌ Without Command Pattern - Tight coupling, no undo!
#include <iostream>#include <string>
class Light {private: std::string location; bool isOn = false;
public: Light(const std::string& location) : location(location) {}
void turnOn() { isOn = true; std::cout << "💡 " << location << " light is ON" << std::endl; }
void turnOff() { isOn = false; std::cout << "💡 " << location << " light is OFF" << std::endl; }};
class Fan {private: std::string location; int speed = 0;
public: Fan(const std::string& location) : location(location) {}
void setSpeed(int speed) { this->speed = speed; std::cout << "🌀 " << location << " fan speed: " << speed << std::endl; }};
class RemoteControl {private: // Problem: Remote knows about ALL device types! Light* light = nullptr; Fan* fan = nullptr;
public: void setLight(Light* light) { this->light = light; }
void setFan(Fan* fan) { this->fan = fan; }
void pressLightOn() { if (light) { light->turnOn(); } }
void pressLightOff() { if (light) { light->turnOff(); } }
void pressFanHigh() { if (fan) { fan->setSpeed(3); } }
void pressFanOff() { if (fan) { fan->setSpeed(0); } }
// Problems: // - Remote tightly coupled to device types // - Need to modify Remote for each new device // - No undo functionality // - No way to queue commands};
// Usageint main() { RemoteControl remote; Light light("Living Room"); remote.setLight(&light); remote.pressLightOn(); // How do we undo? No easy way! return 0;}// ❌ Without Command Pattern - Tight coupling, no undo!
using System;
class Light{ private string location; private bool isOn = false;
public Light(string location) { this.location = location; }
public void TurnOn() { isOn = true; Console.WriteLine($"💡 {location} light is ON"); }
public void TurnOff() { isOn = false; Console.WriteLine($"💡 {location} light is OFF"); }}
class Fan{ private string location; private int speed = 0;
public Fan(string location) { this.location = location; }
public void SetSpeed(int speed) { this.speed = speed; Console.WriteLine($"🌀 {location} fan speed: {speed}"); }}
class RemoteControl{ // Problem: Remote knows about ALL device types! private Light? light = null; private Fan? fan = null;
public void SetLight(Light light) { this.light = light; }
public void SetFan(Fan fan) { this.fan = fan; }
public void PressLightOn() { light?.TurnOn(); }
public void PressLightOff() { light?.TurnOff(); }
public void PressFanHigh() { fan?.SetSpeed(3); }
public void PressFanOff() { fan?.SetSpeed(0); }
// Problems: // - Remote tightly coupled to device types // - Need to modify Remote for each new device // - No undo functionality // - No way to queue commands}
// Usageclass Program{ static void Main() { RemoteControl remote = new RemoteControl(); remote.SetLight(new Light("Living Room")); remote.PressLightOn(); // How do we undo? No easy way! }}package main
import "fmt"
// ❌ Without Command Pattern - Tight coupling, no undo!
type Light struct { location string isOn bool}
func (l *Light) TurnOn() { l.isOn = true; fmt.Printf("💡 %s light is ON\n", l.location) }func (l *Light) TurnOff() { l.isOn = false; fmt.Printf("💡 %s light is OFF\n", l.location) }
type Fan struct { location string speed int}
func (f *Fan) SetSpeed(speed int) { f.speed = speed fmt.Printf("🌀 %s fan speed: %d\n", f.location, speed)}
// Problem: RemoteControl knows about ALL device types!type RemoteControl struct { light *Light fan *Fan}
func (r *RemoteControl) SetLight(l *Light) { r.light = l }func (r *RemoteControl) SetFan(f *Fan) { r.fan = f }func (r *RemoteControl) PressLightOn() { if r.light != nil { r.light.TurnOn() } }func (r *RemoteControl) PressLightOff() { if r.light != nil { r.light.TurnOff() } }func (r *RemoteControl) PressFanHigh() { if r.fan != nil { r.fan.SetSpeed(3) } }
func main() { remote := &RemoteControl{} remote.SetLight(&Light{location: "Living Room"}) remote.PressLightOn() // How do we undo? No easy way!}// The Problemstruct Button;impl Button { fn click_save(&self) { println!("Saving directly"); } fn click_print(&self) { println!("Printing directly"); }}Problems:
- Remote control is tightly coupled to all device types
- Adding new devices requires modifying RemoteControl
- No undo functionality - can’t reverse actions
- No way to queue or log commands
The Solution: Command Pattern
Section titled “The Solution: Command Pattern”Class Structure
Section titled “Class Structure”from abc import ABC, abstractmethodfrom typing import List
# Step 1: Define the Command interfaceclass Command(ABC): """Command interface - all commands implement this"""
@abstractmethod def execute(self) -> None: """Execute the command""" pass
@abstractmethod def undo(self) -> None: """Undo the command""" pass
# Step 2: Create Receiver classes (devices)class Light: """Receiver - the device that performs the action"""
def __init__(self, location: str): self.location = location self.is_on = False
def turn_on(self) -> None: self.is_on = True print(f"💡 {self.location} light is ON")
def turn_off(self) -> None: self.is_on = False print(f"💡 {self.location} light is OFF")
class Fan: """Receiver - fan device"""
def __init__(self, location: str): self.location = location self.speed = 0
def set_speed(self, speed: int) -> None: self.speed = speed if speed == 0: print(f"🌀 {self.location} fan is OFF") else: print(f"🌀 {self.location} fan speed: {speed}")
def get_speed(self) -> int: return self.speed
# Step 3: Create Concrete Commandsclass LightOnCommand(Command): """Concrete command - turn light on"""
def __init__(self, light: Light): self.light = light
def execute(self) -> None: self.light.turn_on()
def undo(self) -> None: self.light.turn_off()
class LightOffCommand(Command): """Concrete command - turn light off"""
def __init__(self, light: Light): self.light = light
def execute(self) -> None: self.light.turn_off()
def undo(self) -> None: self.light.turn_on()
class FanHighCommand(Command): """Concrete command - set fan to high speed"""
def __init__(self, fan: Fan): self.fan = fan self.prev_speed = 0 # Store previous speed for undo
def execute(self) -> None: self.prev_speed = self.fan.get_speed() # Save for undo self.fan.set_speed(3)
def undo(self) -> None: self.fan.set_speed(self.prev_speed)
class FanOffCommand(Command): """Concrete command - turn fan off"""
def __init__(self, fan: Fan): self.fan = fan self.prev_speed = 0
def execute(self) -> None: self.prev_speed = self.fan.get_speed() self.fan.set_speed(0)
def undo(self) -> None: self.fan.set_speed(self.prev_speed)
# Step 4: Create NoCommand (Null Object Pattern)class NoCommand(Command): """Null command - does nothing"""
def execute(self) -> None: pass
def undo(self) -> None: pass
# Step 5: Create the Invokerclass RemoteControl: """Invoker - triggers commands"""
def __init__(self, num_slots: int = 4): self.num_slots = num_slots self.on_commands: List[Command] = [NoCommand()] * num_slots self.off_commands: List[Command] = [NoCommand()] * num_slots self.history: List[Command] = [] # For undo
def set_command(self, slot: int, on_cmd: Command, off_cmd: Command) -> None: """Set commands for a slot""" self.on_commands[slot] = on_cmd self.off_commands[slot] = off_cmd print(f"✅ Slot {slot} configured")
def press_on(self, slot: int) -> None: """Press the ON button for a slot""" print(f"\n🔘 Pressing ON button for slot {slot}") command = self.on_commands[slot] command.execute() self.history.append(command)
def press_off(self, slot: int) -> None: """Press the OFF button for a slot""" print(f"\n🔘 Pressing OFF button for slot {slot}") command = self.off_commands[slot] command.execute() self.history.append(command)
def press_undo(self) -> None: """Undo the last command""" if self.history: print("\n⏪ Pressing UNDO button") command = self.history.pop() command.undo() else: print("\n⏪ Nothing to undo")
# Step 6: Use the patterndef main(): # Create receivers (devices) living_room_light = Light("Living Room") bedroom_fan = Fan("Bedroom")
# Create commands light_on = LightOnCommand(living_room_light) light_off = LightOffCommand(living_room_light) fan_high = FanHighCommand(bedroom_fan) fan_off = FanOffCommand(bedroom_fan)
# Create invoker (remote) remote = RemoteControl()
# Configure remote remote.set_command(0, light_on, light_off) remote.set_command(1, fan_high, fan_off)
# Use the remote remote.press_on(0) # Light ON remote.press_on(1) # Fan HIGH remote.press_off(0) # Light OFF
# Undo operations remote.press_undo() # Light back ON remote.press_undo() # Fan back to previous speed remote.press_undo() # Light back OFF
print("\n✅ Command Pattern: Commands encapsulated with undo support!")
if __name__ == "__main__": main()import java.util.*;
// Step 1: Define the Command interfaceinterface Command { /** * Command interface - all commands implement this */ void execute(); void undo();}
// Step 2: Create Receiver classes (devices)class Light { /** * Receiver - the device that performs the action */ private String location; private boolean isOn = false;
public Light(String location) { this.location = location; }
public void turnOn() { isOn = true; System.out.println("💡 " + location + " light is ON"); }
public void turnOff() { isOn = false; System.out.println("💡 " + location + " light is OFF"); }}
class Fan { /** * Receiver - fan device */ private String location; private int speed = 0;
public Fan(String location) { this.location = location; }
public void setSpeed(int speed) { this.speed = speed; if (speed == 0) { System.out.println("🌀 " + location + " fan is OFF"); } else { System.out.println("🌀 " + location + " fan speed: " + speed); } }
public int getSpeed() { return speed; }}
// Step 3: Create Concrete Commandsclass LightOnCommand implements Command { /** * Concrete command - turn light on */ private Light light;
public LightOnCommand(Light light) { this.light = light; }
@Override public void execute() { light.turnOn(); }
@Override public void undo() { light.turnOff(); }}
class LightOffCommand implements Command { /** * Concrete command - turn light off */ private Light light;
public LightOffCommand(Light light) { this.light = light; }
@Override public void execute() { light.turnOff(); }
@Override public void undo() { light.turnOn(); }}
class FanHighCommand implements Command { /** * Concrete command - set fan to high speed */ private Fan fan; private int prevSpeed = 0; // Store previous speed for undo
public FanHighCommand(Fan fan) { this.fan = fan; }
@Override public void execute() { prevSpeed = fan.getSpeed(); // Save for undo fan.setSpeed(3); }
@Override public void undo() { fan.setSpeed(prevSpeed); }}
class FanOffCommand implements Command { /** * Concrete command - turn fan off */ private Fan fan; private int prevSpeed = 0;
public FanOffCommand(Fan fan) { this.fan = fan; }
@Override public void execute() { prevSpeed = fan.getSpeed(); fan.setSpeed(0); }
@Override public void undo() { fan.setSpeed(prevSpeed); }}
// Step 4: Create NoCommand (Null Object Pattern)class NoCommand implements Command { /** * Null command - does nothing */ @Override public void execute() {}
@Override public void undo() {}}
// Step 5: Create the Invokerclass RemoteControl { /** * Invoker - triggers commands */ private int numSlots; private Command[] onCommands; private Command[] offCommands; private List<Command> history; // For undo
public RemoteControl(int numSlots) { this.numSlots = numSlots; onCommands = new Command[numSlots]; offCommands = new Command[numSlots]; history = new ArrayList<>();
// Initialize with NoCommand NoCommand noCommand = new NoCommand(); for (int i = 0; i < numSlots; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } }
public void setCommand(int slot, Command onCmd, Command offCmd) { // Set commands for a slot onCommands[slot] = onCmd; offCommands[slot] = offCmd; System.out.println("✅ Slot " + slot + " configured"); }
public void pressOn(int slot) { // Press the ON button for a slot System.out.println("\n🔘 Pressing ON button for slot " + slot); Command command = onCommands[slot]; command.execute(); history.add(command); }
public void pressOff(int slot) { // Press the OFF button for a slot System.out.println("\n🔘 Pressing OFF button for slot " + slot); Command command = offCommands[slot]; command.execute(); history.add(command); }
public void pressUndo() { // Undo the last command if (!history.isEmpty()) { System.out.println("\n⏪ Pressing UNDO button"); Command command = history.remove(history.size() - 1); command.undo(); } else { System.out.println("\n⏪ Nothing to undo"); } }}
// Step 6: Use the patternpublic class Main { public static void main(String[] args) { // Create receivers (devices) Light livingRoomLight = new Light("Living Room"); Fan bedroomFan = new Fan("Bedroom");
// Create commands LightOnCommand lightOn = new LightOnCommand(livingRoomLight); LightOffCommand lightOff = new LightOffCommand(livingRoomLight); FanHighCommand fanHigh = new FanHighCommand(bedroomFan); FanOffCommand fanOff = new FanOffCommand(bedroomFan);
// Create invoker (remote) RemoteControl remote = new RemoteControl(4);
// Configure remote remote.setCommand(0, lightOn, lightOff); remote.setCommand(1, fanHigh, fanOff);
// Use the remote remote.pressOn(0); // Light ON remote.pressOn(1); // Fan HIGH remote.pressOff(0); // Light OFF
// Undo operations remote.pressUndo(); // Light back ON remote.pressUndo(); // Fan back to previous speed remote.pressUndo(); // Light back OFF
System.out.println("\n✅ Command Pattern: Commands encapsulated with undo support!"); }}// Complete TypeScript implementation following the same structure// Showing key components for brevity
interface Command { execute(): void; undo(): void;}
class Light { private isOn: boolean = false;
constructor(private location: string) {}
turnOn(): void { this.isOn = true; console.log(`💡 ${this.location} light is ON`); }
turnOff(): void { this.isOn = false; console.log(`💡 ${this.location} light is OFF`); }}
class Fan { private speed: number = 0;
constructor(private location: string) {}
setSpeed(speed: number): void { this.speed = speed; console.log(speed === 0 ? `🌀 ${this.location} fan is OFF` : `🌀 ${this.location} fan speed: ${speed}`); }
getSpeed(): number { return this.speed; }}
class LightOnCommand implements Command { constructor(private light: Light) {}
execute(): void { this.light.turnOn(); }
undo(): void { this.light.turnOff(); }}
class LightOffCommand implements Command { constructor(private light: Light) {}
execute(): void { this.light.turnOff(); }
undo(): void { this.light.turnOn(); }}
class FanHighCommand implements Command { private prevSpeed: number = 0;
constructor(private fan: Fan) {}
execute(): void { this.prevSpeed = this.fan.getSpeed(); this.fan.setSpeed(3); }
undo(): void { this.fan.setSpeed(this.prevSpeed); }}
class NoCommand implements Command { execute(): void {} undo(): void {}}
class RemoteControl { private onCommands: Command[]; private offCommands: Command[]; private history: Command[] = [];
constructor(numSlots: number = 4) { this.onCommands = Array(numSlots).fill(new NoCommand()); this.offCommands = Array(numSlots).fill(new NoCommand()); }
setCommand(slot: number, onCmd: Command, offCmd: Command): void { this.onCommands[slot] = onCmd; this.offCommands[slot] = offCmd; console.log(`✅ Slot ${slot} configured`); }
pressOn(slot: number): void { console.log(`\n🔘 Pressing ON button for slot ${slot}`); const command = this.onCommands[slot]; command.execute(); this.history.push(command); }
pressOff(slot: number): void { console.log(`\n🔘 Pressing OFF button for slot ${slot}`); const command = this.offCommands[slot]; command.execute(); this.history.push(command); }
pressUndo(): void { if (this.history.length > 0) { console.log("\n⏪ Pressing UNDO button"); const command = this.history.pop()!; command.undo(); } else { console.log("\n⏪ Nothing to undo"); } }}
// Usageconst livingRoomLight = new Light("Living Room");const bedroomFan = new Fan("Bedroom");
const lightOn = new LightOnCommand(livingRoomLight);const lightOff = new LightOffCommand(livingRoomLight);const fanHigh = new FanHighCommand(bedroomFan);
const remote = new RemoteControl();remote.setCommand(0, lightOn, lightOff);remote.setCommand(1, fanHigh, new NoCommand());
remote.pressOn(0); // Light ONremote.pressOn(1); // Fan HIGHremote.pressUndo(); // Undo fanremote.pressUndo(); // Undo lightconsole.log("\n✅ Command Pattern: Commands encapsulated with undo support!");// Complete C++ implementation following the same structure// Showing key components for brevity
#include <iostream>#include <string>#include <vector>#include <memory>
class Command {public: virtual ~Command() = default; virtual void execute() = 0; virtual void undo() = 0;};
class Light {private: std::string location; bool isOn = false;
public: Light(const std::string& location) : location(location) {}
void turnOn() { isOn = true; std::cout << "💡 " << location << " light is ON" << std::endl; }
void turnOff() { isOn = false; std::cout << "💡 " << location << " light is OFF" << std::endl; }};
class Fan {private: std::string location; int speed = 0;
public: Fan(const std::string& location) : location(location) {}
void setSpeed(int speed) { this->speed = speed; if (speed == 0) { std::cout << "🌀 " << location << " fan is OFF" << std::endl; } else { std::cout << "🌀 " << location << " fan speed: " << speed << std::endl; } }
int getSpeed() const { return speed; }};
class LightOnCommand : public Command {private: Light* light;
public: LightOnCommand(Light* light) : light(light) {}
void execute() override { light->turnOn(); }
void undo() override { light->turnOff(); }};
class LightOffCommand : public Command {private: Light* light;
public: LightOffCommand(Light* light) : light(light) {}
void execute() override { light->turnOff(); }
void undo() override { light->turnOn(); }};
class FanHighCommand : public Command {private: Fan* fan; int prevSpeed = 0;
public: FanHighCommand(Fan* fan) : fan(fan) {}
void execute() override { prevSpeed = fan->getSpeed(); fan->setSpeed(3); }
void undo() override { fan->setSpeed(prevSpeed); }};
class NoCommand : public Command {public: void execute() override {} void undo() override {}};
class RemoteControl {private: std::vector<std::shared_ptr<Command>> onCommands; std::vector<std::shared_ptr<Command>> offCommands; std::vector<std::shared_ptr<Command>> history;
public: RemoteControl(int numSlots = 4) { auto noCmd = std::make_shared<NoCommand>(); onCommands.resize(numSlots, noCmd); offCommands.resize(numSlots, noCmd); }
void setCommand(int slot, std::shared_ptr<Command> onCmd, std::shared_ptr<Command> offCmd) { onCommands[slot] = onCmd; offCommands[slot] = offCmd; std::cout << "✅ Slot " << slot << " configured" << std::endl; }
void pressOn(int slot) { std::cout << "\n🔘 Pressing ON button for slot " << slot << std::endl; auto command = onCommands[slot]; command->execute(); history.push_back(command); }
void pressOff(int slot) { std::cout << "\n🔘 Pressing OFF button for slot " << slot << std::endl; auto command = offCommands[slot]; command->execute(); history.push_back(command); }
void pressUndo() { if (!history.empty()) { std::cout << "\n⏪ Pressing UNDO button" << std::endl; auto command = history.back(); command->undo(); history.pop_back(); } else { std::cout << "\n⏪ Nothing to undo" << std::endl; } }};
// Usage exampleint main() { Light livingRoomLight("Living Room"); Fan bedroomFan("Bedroom");
auto lightOn = std::make_shared<LightOnCommand>(&livingRoomLight); auto lightOff = std::make_shared<LightOffCommand>(&livingRoomLight); auto fanHigh = std::make_shared<FanHighCommand>(&bedroomFan); auto noCmd = std::make_shared<NoCommand>();
RemoteControl remote; remote.setCommand(0, lightOn, lightOff); remote.setCommand(1, fanHigh, noCmd);
remote.pressOn(0); remote.pressOn(1); remote.pressUndo(); remote.pressUndo();
std::cout << "\n✅ Command Pattern: Commands encapsulated with undo support!" << std::endl; return 0;}// Complete C# implementation following the same structure// Showing key components for brevity
using System;using System.Collections.Generic;
public interface ICommand{ void Execute(); void Undo();}
public class Light{ private string location; private bool isOn = false;
public Light(string location) { this.location = location; }
public void TurnOn() { isOn = true; Console.WriteLine($"💡 {location} light is ON"); }
public void TurnOff() { isOn = false; Console.WriteLine($"💡 {location} light is OFF"); }}
public class Fan{ private string location; private int speed = 0;
public Fan(string location) { this.location = location; }
public void SetSpeed(int speed) { this.speed = speed; Console.WriteLine(speed == 0 ? $"🌀 {location} fan is OFF" : $"🌀 {location} fan speed: {speed}"); }
public int GetSpeed() => speed;}
public class LightOnCommand : ICommand{ private Light light;
public LightOnCommand(Light light) { this.light = light; }
public void Execute() { light.TurnOn(); }
public void Undo() { light.TurnOff(); }}
public class LightOffCommand : ICommand{ private Light light;
public LightOffCommand(Light light) { this.light = light; }
public void Execute() { light.TurnOff(); }
public void Undo() { light.TurnOn(); }}
public class FanHighCommand : ICommand{ private Fan fan; private int prevSpeed = 0;
public FanHighCommand(Fan fan) { this.fan = fan; }
public void Execute() { prevSpeed = fan.GetSpeed(); fan.SetSpeed(3); }
public void Undo() { fan.SetSpeed(prevSpeed); }}
public class NoCommand : ICommand{ public void Execute() { } public void Undo() { }}
public class RemoteControl{ private ICommand[] onCommands; private ICommand[] offCommands; private List<ICommand> history = new List<ICommand>();
public RemoteControl(int numSlots = 4) { onCommands = new ICommand[numSlots]; offCommands = new ICommand[numSlots]; var noCmd = new NoCommand(); for (int i = 0; i < numSlots; i++) { onCommands[i] = noCmd; offCommands[i] = noCmd; } }
public void SetCommand(int slot, ICommand onCmd, ICommand offCmd) { onCommands[slot] = onCmd; offCommands[slot] = offCmd; Console.WriteLine($"✅ Slot {slot} configured"); }
public void PressOn(int slot) { Console.WriteLine($"\n🔘 Pressing ON button for slot {slot}"); ICommand command = onCommands[slot]; command.Execute(); history.Add(command); }
public void PressOff(int slot) { Console.WriteLine($"\n🔘 Pressing OFF button for slot {slot}"); ICommand command = offCommands[slot]; command.Execute(); history.Add(command); }
public void PressUndo() { if (history.Count > 0) { Console.WriteLine("\n⏪ Pressing UNDO button"); ICommand command = history[history.Count - 1]; command.Undo(); history.RemoveAt(history.Count - 1); } else { Console.WriteLine("\n⏪ Nothing to undo"); } }}
// Usageclass Program{ static void Main() { Light livingRoomLight = new Light("Living Room"); Fan bedroomFan = new Fan("Bedroom");
ICommand lightOn = new LightOnCommand(livingRoomLight); ICommand lightOff = new LightOffCommand(livingRoomLight); ICommand fanHigh = new FanHighCommand(bedroomFan);
RemoteControl remote = new RemoteControl(); remote.SetCommand(0, lightOn, lightOff); remote.SetCommand(1, fanHigh, new NoCommand());
remote.PressOn(0); remote.PressOn(1); remote.PressUndo(); remote.PressUndo();
Console.WriteLine("\n✅ Command Pattern: Commands encapsulated with undo support!"); }}package main
import "fmt"
// Command interfacetype Command interface { Execute() Undo()}
// Receivertype Light struct { location string isOn bool}
func (l *Light) TurnOn() { l.isOn = true; fmt.Printf("💡 %s light is ON\n", l.location) }func (l *Light) TurnOff() { l.isOn = false; fmt.Printf("💡 %s light is OFF\n", l.location) }
// Concrete Commandstype LightOnCommand struct{ light *Light }
func (c *LightOnCommand) Execute() { c.light.TurnOn() }func (c *LightOnCommand) Undo() { c.light.TurnOff() }
type LightOffCommand struct{ light *Light }
func (c *LightOffCommand) Execute() { c.light.TurnOff() }func (c *LightOffCommand) Undo() { c.light.TurnOn() }
// Null objecttype NoCommand struct{}
func (n *NoCommand) Execute() {}func (n *NoCommand) Undo() {}
// Invokertype RemoteControl struct { onCommands []Command offCommands []Command history []Command}
func NewRemoteControl(numSlots int) *RemoteControl { rc := &RemoteControl{ onCommands: make([]Command, numSlots), offCommands: make([]Command, numSlots), } noCmd := &NoCommand{} for i := range rc.onCommands { rc.onCommands[i] = noCmd rc.offCommands[i] = noCmd } return rc}
func (rc *RemoteControl) SetCommand(slot int, on, off Command) { rc.onCommands[slot] = on rc.offCommands[slot] = off fmt.Printf("✅ Slot %d configured\n", slot)}
func (rc *RemoteControl) PressOn(slot int) { cmd := rc.onCommands[slot] cmd.Execute() rc.history = append(rc.history, cmd)}
func (rc *RemoteControl) PressUndo() { if len(rc.history) == 0 { fmt.Println("⏪ Nothing to undo"); return } cmd := rc.history[len(rc.history)-1] cmd.Undo() rc.history = rc.history[:len(rc.history)-1]}
func main() { light := &Light{location: "Living Room"} remote := NewRemoteControl(4) remote.SetCommand(0, &LightOnCommand{light}, &LightOffCommand{light}) remote.PressOn(0) remote.PressUndo() fmt.Println("\n✅ Command Pattern: Commands encapsulated with undo support!")}// Class Structuretrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}Real-World Software Example: Text Editor with Undo/Redo
Section titled “Real-World Software Example: Text Editor with Undo/Redo”Now let’s see a realistic software example - a text editor that supports full undo/redo functionality.
The Problem
Section titled “The Problem”You’re building a text editor that needs to support multiple operations (typing, deleting, formatting) with full undo/redo capability. Without Command Pattern:
# ❌ Without Command Pattern - Undo is a nightmare!
class TextEditor: def __init__(self): self.content = "" # Problem: Need to track every change manually! self.history = []
def type_text(self, text: str, position: int): # Insert text at position old_content = self.content self.content = self.content[:position] + text + self.content[position:] # Problem: How to undo? Store entire content? self.history.append(("type", position, text, old_content)) print(f"Typed: '{text}'")
def delete_text(self, start: int, end: int): # Delete text in range old_content = self.content deleted = self.content[start:end] self.content = self.content[:start] + self.content[end:] # Problem: Complex undo tracking self.history.append(("delete", start, end, deleted, old_content)) print(f"Deleted: '{deleted}'")
def undo(self): if not self.history: return
# Problem: Complex logic for each operation type! last_op = self.history.pop()
if last_op[0] == "type": # Undo type - restore old content self.content = last_op[3] elif last_op[0] == "delete": # Undo delete - restore old content self.content = last_op[4] # Problem: Need to handle EVERY operation type! # - What about bold? italic? find-replace? # - Code becomes unmaintainable!
# Usageeditor = TextEditor()editor.type_text("Hello", 0)editor.type_text(" World", 5)editor.delete_text(5, 11) # Delete " World"editor.undo() # Should restore " World" - but complex!// ❌ Without Command Pattern - Undo is a nightmare!
import java.util.*;
class TextEditor { private StringBuilder content = new StringBuilder(); // Problem: Need to track every change manually! private List<Object[]> history = new ArrayList<>();
public void typeText(String text, int position) { // Insert text at position String oldContent = content.toString(); content.insert(position, text); // Problem: How to undo? Store entire content? history.add(new Object[]{"type", position, text, oldContent}); System.out.println("Typed: '" + text + "'"); }
public void deleteText(int start, int end) { // Delete text in range String oldContent = content.toString(); String deleted = content.substring(start, end); content.delete(start, end); // Problem: Complex undo tracking history.add(new Object[]{"delete", start, end, deleted, oldContent}); System.out.println("Deleted: '" + deleted + "'"); }
public void undo() { if (history.isEmpty()) { return; }
// Problem: Complex logic for each operation type! Object[] lastOp = history.remove(history.size() - 1);
if (lastOp[0].equals("type")) { // Undo type - restore old content content = new StringBuilder((String) lastOp[3]); } else if (lastOp[0].equals("delete")) { // Undo delete - restore old content content = new StringBuilder((String) lastOp[4]); } // Problem: Need to handle EVERY operation type! // - What about bold? italic? find-replace? // - Code becomes unmaintainable! }}
// Usagepublic class Main { public static void main(String[] args) { TextEditor editor = new TextEditor(); editor.typeText("Hello", 0); editor.typeText(" World", 5); editor.deleteText(5, 11); // Delete " World" editor.undo(); // Should restore " World" - but complex! }}// ❌ Without Command Pattern - Complex undo, no redo!
class TextEditor { private content: string = ""; private history: Array<[string, any, any, any?]> = [];
typeText(text: string, position: number): void { const oldContent = this.content; this.content = this.content.slice(0, position) + text + this.content.slice(position); // Problem: Manual tracking! this.history.push(["type", position, text, oldContent]); console.log(`Typed: '${text}'`); }
deleteText(start: number, end: number): void { const oldContent = this.content; const deleted = this.content.substring(start, end); this.content = this.content.slice(0, start) + this.content.slice(end); // Problem: Complex undo tracking this.history.push(["delete", start, end, deleted, oldContent]); console.log(`Deleted: '${deleted}'`); }
undo(): void { if (this.history.length === 0) return;
// Problem: Complex logic for each operation type! const lastOp = this.history.pop()!;
if (lastOp[0] === "type") { this.content = lastOp[3]; } else if (lastOp[0] === "delete") { this.content = lastOp[4]; } // Problem: Need to handle EVERY operation type! }}
// Usageconst editor = new TextEditor();editor.typeText("Hello", 0);editor.typeText(" World", 5);editor.deleteText(5, 11);editor.undo();// ❌ Without Command Pattern - Complex undo, no redo!
#include <iostream>#include <string>#include <vector>#include <tuple>
class TextEditor {private: std::string content; std::vector<std::tuple<std::string, int, std::string, std::string>> history;
public: void typeText(const std::string& text, int position) { std::string oldContent = content; content.insert(position, text); // Problem: Manual tracking! history.push_back(std::make_tuple("type", position, text, oldContent)); std::cout << "Typed: '" << text << "'" << std::endl; }
void deleteText(int start, int end) { std::string oldContent = content; std::string deleted = content.substr(start, end - start); content.erase(start, end - start); // Problem: Complex undo tracking history.push_back(std::make_tuple("delete", start, deleted, oldContent)); std::cout << "Deleted: '" << deleted << "'" << std::endl; }
void undo() { if (history.empty()) return;
// Problem: Complex logic for each operation type! auto lastOp = history.back(); history.pop_back();
if (std::get<0>(lastOp) == "type") { content = std::get<3>(lastOp); } else if (std::get<0>(lastOp) == "delete") { content = std::get<3>(lastOp); } // Problem: Need to handle EVERY operation type! }};
// Usageint main() { TextEditor editor; editor.typeText("Hello", 0); editor.typeText(" World", 5); editor.deleteText(5, 11); editor.undo(); return 0;}// ❌ Without Command Pattern - Complex undo, no redo!
using System;using System.Text;using System.Collections.Generic;
class TextEditor{ private StringBuilder content = new StringBuilder(); private List<object[]> history = new List<object[]>();
public void TypeText(string text, int position) { string oldContent = content.ToString(); content.Insert(position, text); // Problem: Manual tracking! history.Add(new object[] { "type", position, text, oldContent }); Console.WriteLine($"Typed: '{text}'"); }
public void DeleteText(int start, int end) { string oldContent = content.ToString(); string deleted = content.ToString().Substring(start, end - start); content.Remove(start, end - start); // Problem: Complex undo tracking history.Add(new object[] { "delete", start, end, deleted, oldContent }); Console.WriteLine($"Deleted: '{deleted}'"); }
public void Undo() { if (history.Count == 0) return;
// Problem: Complex logic for each operation type! object[] lastOp = history[history.Count - 1]; history.RemoveAt(history.Count - 1);
if ((string)lastOp[0] == "type") { content = new StringBuilder((string)lastOp[3]); } else if ((string)lastOp[0] == "delete") { content = new StringBuilder((string)lastOp[4]); } // Problem: Need to handle EVERY operation type! }}
// Usageclass Program{ static void Main() { TextEditor editor = new TextEditor(); editor.TypeText("Hello", 0); editor.TypeText(" World", 5); editor.DeleteText(5, 11); editor.Undo(); }}package main
import "fmt"
// ❌ Without Command Pattern - Complex undo, no redo!type TextEditorBad struct { content string history []map[string]any}
func (e *TextEditorBad) TypeText(text string, pos int) { old := e.content e.content = e.content[:pos] + text + e.content[pos:] e.history = append(e.history, map[string]any{"op": "type", "pos": pos, "text": text, "old": old}) fmt.Printf("Typed: '%s'\n", text)}
func (e *TextEditorBad) Undo() { if len(e.history) == 0 { return } last := e.history[len(e.history)-1] e.history = e.history[:len(e.history)-1] // Problem: Complex logic for each operation type! if last["op"] == "type" { e.content = last["old"].(string) }}// The Problemstruct Button;impl Button { fn click_save(&self) { println!("Saving directly"); } fn click_print(&self) { println!("Printing directly"); }}Problems:
- Complex undo tracking for each operation type
- Need to modify editor for each new operation
- History management is complex and error-prone
- No redo support
- Hard to serialize/log operations
The Solution: Command Pattern
Section titled “The Solution: Command Pattern”Class Structure
Section titled “Class Structure”from abc import ABC, abstractmethodfrom typing import List, Optionalfrom dataclasses import dataclass, field
# Step 1: Create the Document (Receiver)class Document: """Receiver - the document being edited"""
def __init__(self): self._content = ""
def insert(self, position: int, text: str) -> None: """Insert text at position""" self._content = self._content[:position] + text + self._content[position:]
def delete(self, start: int, end: int) -> str: """Delete text in range and return deleted text""" deleted = self._content[start:end] self._content = self._content[:start] + self._content[end:] return deleted
def get_content(self) -> str: """Get document content""" return self._content
def get_length(self) -> int: """Get document length""" return len(self._content)
# Step 2: Define the Command interfaceclass EditorCommand(ABC): """Command interface for editor operations"""
@abstractmethod def execute(self) -> None: """Execute the command""" pass
@abstractmethod def undo(self) -> None: """Undo the command""" pass
@property @abstractmethod def description(self) -> str: """Description of the command for logging""" pass
# Step 3: Create Concrete Commandsclass TypeCommand(EditorCommand): """Command to type/insert text"""
def __init__(self, document: Document, text: str, position: int): self._document = document self._text = text self._position = position
def execute(self) -> None: self._document.insert(self._position, self._text)
def undo(self) -> None: # Delete the text that was typed self._document.delete(self._position, self._position + len(self._text))
@property def description(self) -> str: return f"Type '{self._text}' at position {self._position}"
class DeleteCommand(EditorCommand): """Command to delete text"""
def __init__(self, document: Document, start: int, end: int): self._document = document self._start = start self._end = end self._deleted_text: str = "" # Store for undo
def execute(self) -> None: # Store deleted text for undo self._deleted_text = self._document.delete(self._start, self._end)
def undo(self) -> None: # Re-insert the deleted text self._document.insert(self._start, self._deleted_text)
@property def description(self) -> str: return f"Delete text from {self._start} to {self._end}"
class ReplaceCommand(EditorCommand): """Command to replace text"""
def __init__(self, document: Document, start: int, end: int, new_text: str): self._document = document self._start = start self._end = end self._new_text = new_text self._old_text: str = "" # Store for undo
def execute(self) -> None: # Store old text and replace self._old_text = self._document.delete(self._start, self._end) self._document.insert(self._start, self._new_text)
def undo(self) -> None: # Delete new text and restore old self._document.delete(self._start, self._start + len(self._new_text)) self._document.insert(self._start, self._old_text)
@property def description(self) -> str: return f"Replace '{self._old_text}' with '{self._new_text}'"
# Step 4: Create Macro Command (Composite Command)class MacroCommand(EditorCommand): """Composite command - executes multiple commands"""
def __init__(self, commands: List[EditorCommand], name: str = "Macro"): self._commands = commands self._name = name
def execute(self) -> None: for command in self._commands: command.execute()
def undo(self) -> None: # Undo in reverse order! for command in reversed(self._commands): command.undo()
@property def description(self) -> str: return f"Macro: {self._name} ({len(self._commands)} commands)"
# Step 5: Create the Editor (Invoker)class TextEditor: """Invoker - manages command execution and undo/redo"""
def __init__(self): self._document = Document() self._undo_stack: List[EditorCommand] = [] self._redo_stack: List[EditorCommand] = []
def execute_command(self, command: EditorCommand) -> None: """Execute a command and add to history""" command.execute() self._undo_stack.append(command) self._redo_stack.clear() # Clear redo stack on new command print(f"✅ Executed: {command.description}") print(f" Content: '{self._document.get_content()}'")
def undo(self) -> bool: """Undo the last command""" if not self._undo_stack: print("⚠️ Nothing to undo") return False
command = self._undo_stack.pop() command.undo() self._redo_stack.append(command) print(f"⏪ Undone: {command.description}") print(f" Content: '{self._document.get_content()}'") return True
def redo(self) -> bool: """Redo the last undone command""" if not self._redo_stack: print("⚠️ Nothing to redo") return False
command = self._redo_stack.pop() command.execute() self._undo_stack.append(command) print(f"⏩ Redone: {command.description}") print(f" Content: '{self._document.get_content()}'") return True
def get_content(self) -> str: """Get document content""" return self._document.get_content()
def get_document(self) -> Document: """Get document for creating commands""" return self._document
def get_history(self) -> List[str]: """Get command history""" return [cmd.description for cmd in self._undo_stack]
# Step 6: Use the patterndef main(): # Create editor editor = TextEditor() doc = editor.get_document()
print("=" * 50) print("Text Editor with Undo/Redo (Command Pattern)") print("=" * 50)
# Type some text editor.execute_command(TypeCommand(doc, "Hello", 0)) editor.execute_command(TypeCommand(doc, " World", 5)) editor.execute_command(TypeCommand(doc, "!", 11))
# Delete some text editor.execute_command(DeleteCommand(doc, 5, 11)) # Delete " World"
# Replace text editor.execute_command(ReplaceCommand(doc, 0, 5, "Hi"))
print("\n" + "-" * 50) print("Testing Undo/Redo") print("-" * 50)
# Undo operations editor.undo() # Undo replace editor.undo() # Undo delete editor.undo() # Undo "!"
# Redo some operations editor.redo() # Redo "!" editor.redo() # Redo delete
print("\n" + "-" * 50) print("Testing Macro Command") print("-" * 50)
# Create a macro command doc = editor.get_document() macro = MacroCommand([ TypeCommand(doc, " - Edited", editor.get_document().get_length()), TypeCommand(doc, " (v2)", editor.get_document().get_length() + 9), ], "Add version tag")
editor.execute_command(macro)
# Undo entire macro at once editor.undo()
print("\n" + "-" * 50) print("Command History:") print("-" * 50) for i, desc in enumerate(editor.get_history(), 1): print(f" {i}. {desc}")
print("\n✅ Command Pattern: Full undo/redo with command history!")
if __name__ == "__main__": main()import java.util.*;
// Step 1: Create the Document (Receiver)class Document { /** * Receiver - the document being edited */ private StringBuilder content = new StringBuilder();
public void insert(int position, String text) { // Insert text at position content.insert(position, text); }
public String delete(int start, int end) { // Delete text in range and return deleted text String deleted = content.substring(start, end); content.delete(start, end); return deleted; }
public String getContent() { // Get document content return content.toString(); }
public int getLength() { // Get document length return content.length(); }}
// Step 2: Define the Command interfaceinterface EditorCommand { /** * Command interface for editor operations */ void execute(); void undo(); String getDescription();}
// Step 3: Create Concrete Commandsclass TypeCommand implements EditorCommand { /** * Command to type/insert text */ private Document document; private String text; private int position;
public TypeCommand(Document document, String text, int position) { this.document = document; this.text = text; this.position = position; }
@Override public void execute() { document.insert(position, text); }
@Override public void undo() { // Delete the text that was typed document.delete(position, position + text.length()); }
@Override public String getDescription() { return "Type '" + text + "' at position " + position; }}
class DeleteCommand implements EditorCommand { /** * Command to delete text */ private Document document; private int start; private int end; private String deletedText = ""; // Store for undo
public DeleteCommand(Document document, int start, int end) { this.document = document; this.start = start; this.end = end; }
@Override public void execute() { // Store deleted text for undo deletedText = document.delete(start, end); }
@Override public void undo() { // Re-insert the deleted text document.insert(start, deletedText); }
@Override public String getDescription() { return "Delete text from " + start + " to " + end; }}
class ReplaceCommand implements EditorCommand { /** * Command to replace text */ private Document document; private int start; private int end; private String newText; private String oldText = ""; // Store for undo
public ReplaceCommand(Document document, int start, int end, String newText) { this.document = document; this.start = start; this.end = end; this.newText = newText; }
@Override public void execute() { // Store old text and replace oldText = document.delete(start, end); document.insert(start, newText); }
@Override public void undo() { // Delete new text and restore old document.delete(start, start + newText.length()); document.insert(start, oldText); }
@Override public String getDescription() { return "Replace '" + oldText + "' with '" + newText + "'"; }}
// Step 4: Create Macro Command (Composite Command)class MacroCommand implements EditorCommand { /** * Composite command - executes multiple commands */ private List<EditorCommand> commands; private String name;
public MacroCommand(List<EditorCommand> commands, String name) { this.commands = commands; this.name = name; }
@Override public void execute() { for (EditorCommand command : commands) { command.execute(); } }
@Override public void undo() { // Undo in reverse order! for (int i = commands.size() - 1; i >= 0; i--) { commands.get(i).undo(); } }
@Override public String getDescription() { return "Macro: " + name + " (" + commands.size() + " commands)"; }}
// Step 5: Create the Editor (Invoker)class TextEditor { /** * Invoker - manages command execution and undo/redo */ private Document document; private List<EditorCommand> undoStack; private List<EditorCommand> redoStack;
public TextEditor() { document = new Document(); undoStack = new ArrayList<>(); redoStack = new ArrayList<>(); }
public void executeCommand(EditorCommand command) { // Execute a command and add to history command.execute(); undoStack.add(command); redoStack.clear(); // Clear redo stack on new command System.out.println("✅ Executed: " + command.getDescription()); System.out.println(" Content: '" + document.getContent() + "'"); }
public boolean undo() { // Undo the last command if (undoStack.isEmpty()) { System.out.println("⚠️ Nothing to undo"); return false; }
EditorCommand command = undoStack.remove(undoStack.size() - 1); command.undo(); redoStack.add(command); System.out.println("⏪ Undone: " + command.getDescription()); System.out.println(" Content: '" + document.getContent() + "'"); return true; }
public boolean redo() { // Redo the last undone command if (redoStack.isEmpty()) { System.out.println("⚠️ Nothing to redo"); return false; }
EditorCommand command = redoStack.remove(redoStack.size() - 1); command.execute(); undoStack.add(command); System.out.println("⏩ Redone: " + command.getDescription()); System.out.println(" Content: '" + document.getContent() + "'"); return true; }
public String getContent() { return document.getContent(); }
public Document getDocument() { return document; }
public List<String> getHistory() { List<String> history = new ArrayList<>(); for (EditorCommand cmd : undoStack) { history.add(cmd.getDescription()); } return history; }}
// Step 6: Use the patternpublic class Main { public static void main(String[] args) { // Create editor TextEditor editor = new TextEditor(); Document doc = editor.getDocument();
System.out.println("=".repeat(50)); System.out.println("Text Editor with Undo/Redo (Command Pattern)"); System.out.println("=".repeat(50));
// Type some text editor.executeCommand(new TypeCommand(doc, "Hello", 0)); editor.executeCommand(new TypeCommand(doc, " World", 5)); editor.executeCommand(new TypeCommand(doc, "!", 11));
// Delete some text editor.executeCommand(new DeleteCommand(doc, 5, 11)); // Delete " World"
// Replace text editor.executeCommand(new ReplaceCommand(doc, 0, 5, "Hi"));
System.out.println("\n" + "-".repeat(50)); System.out.println("Testing Undo/Redo"); System.out.println("-".repeat(50));
// Undo operations editor.undo(); // Undo replace editor.undo(); // Undo delete editor.undo(); // Undo "!"
// Redo some operations editor.redo(); // Redo "!" editor.redo(); // Redo delete
System.out.println("\n" + "-".repeat(50)); System.out.println("Testing Macro Command"); System.out.println("-".repeat(50));
// Create a macro command doc = editor.getDocument(); int length = doc.getLength(); MacroCommand macro = new MacroCommand(Arrays.asList( new TypeCommand(doc, " - Edited", length), new TypeCommand(doc, " (v2)", length + 9) ), "Add version tag");
editor.executeCommand(macro);
// Undo entire macro at once editor.undo();
System.out.println("\n" + "-".repeat(50)); System.out.println("Command History:"); System.out.println("-".repeat(50)); int i = 1; for (String desc : editor.getHistory()) { System.out.println(" " + i++ + ". " + desc); }
System.out.println("\n✅ Command Pattern: Full undo/redo with command history!"); }}// Complete TypeScript implementation of text editor with undo/redo// Showing key structure - full implementation follows pattern above
abstract class EditorCommand { abstract execute(): void; abstract undo(): void; abstract description: string;}
class Document { private content: string = "";
insert(position: number, text: string): void { this.content = this.content.slice(0, position) + text + this.content.slice(position); }
delete(start: number, end: number): string { const deleted = this.content.substring(start, end); this.content = this.content.slice(0, start) + this.content.slice(end); return deleted; }
getContent(): string { return this.content; }}
class InsertCommand extends EditorCommand { private deletedText: string = "";
constructor( private document: Document, private position: number, private text: string ) { super(); }
execute(): void { this.document.insert(this.position, this.text); }
undo(): void { this.document.delete(this.position, this.position + this.text.length); }
get description(): string { return `Insert '${this.text}' at ${this.position}`; }}
class TextEditor { private document: Document = new Document(); private undoStack: EditorCommand[] = []; private redoStack: EditorCommand[] = [];
executeCommand(command: EditorCommand): void { command.execute(); this.undoStack.push(command); this.redoStack = []; console.log(`✅ Executed: ${command.description}`); }
undo(): boolean { if (this.undoStack.length === 0) { console.log("⚠️ Nothing to undo"); return false; } const command = this.undoStack.pop()!; command.undo(); this.redoStack.push(command); console.log(`⏪ Undone: ${command.description}`); return true; }
redo(): boolean { if (this.redoStack.length === 0) { console.log("⚠️ Nothing to redo"); return false; } const command = this.redoStack.pop()!; command.execute(); this.undoStack.push(command); console.log(`⏩ Redone: ${command.description}`); return true; }}
// Usageconst editor = new TextEditor();editor.executeCommand(new InsertCommand(editor['document'], 0, "Hello"));editor.undo();editor.redo();console.log("\n✅ Command Pattern: Full undo/redo with command history!");// Complete C++ implementation of text editor with undo/redo// Showing key structure - uses smart pointers for memory management
#include <iostream>#include <string>#include <vector>#include <memory>
class Document {private: std::string content;
public: void insert(int position, const std::string& text) { content.insert(position, text); }
std::string deleteRange(int start, int end) { std::string deleted = content.substr(start, end - start); content.erase(start, end - start); return deleted; }
std::string getContent() const { return content; }};
class EditorCommand {public: virtual ~EditorCommand() = default; virtual void execute() = 0; virtual void undo() = 0; virtual std::string description() const = 0;};
class InsertCommand : public EditorCommand {private: Document* document; int position; std::string text;
public: InsertCommand(Document* doc, int pos, const std::string& txt) : document(doc), position(pos), text(txt) {}
void execute() override { document->insert(position, text); }
void undo() override { document->deleteRange(position, position + text.length()); }
std::string description() const override { return "Insert '" + text + "' at " + std::to_string(position); }};
class TextEditor {private: Document document; std::vector<std::shared_ptr<EditorCommand>> undoStack; std::vector<std::shared_ptr<EditorCommand>> redoStack;
public: void executeCommand(std::shared_ptr<EditorCommand> command) { command->execute(); undoStack.push_back(command); redoStack.clear(); std::cout << "✅ Executed: " << command->description() << std::endl; }
bool undo() { if (undoStack.empty()) { std::cout << "⚠️ Nothing to undo" << std::endl; return false; } auto command = undoStack.back(); command->undo(); undoStack.pop_back(); redoStack.push_back(command); std::cout << "⏪ Undone: " << command->description() << std::endl; return true; }
bool redo() { if (redoStack.empty()) { std::cout << "⚠️ Nothing to redo" << std::endl; return false; } auto command = redoStack.back(); command->execute(); redoStack.pop_back(); undoStack.push_back(command); std::cout << "⏩ Redone: " << command->description() << std::endl; return true; }
Document& getDocument() { return document; }};
// Usageint main() { TextEditor editor; auto cmd = std::make_shared<InsertCommand>(&editor.getDocument(), 0, "Hello"); editor.executeCommand(cmd); editor.undo(); editor.redo(); std::cout << "\n✅ Command Pattern: Full undo/redo with command history!" << std::endl; return 0;}// Complete C# implementation of text editor with undo/redo// Showing key structure
using System;using System.Text;using System.Collections.Generic;
public abstract class EditorCommand{ public abstract void Execute(); public abstract void Undo(); public abstract string Description { get; }}
public class Document{ private StringBuilder content = new StringBuilder();
public void Insert(int position, string text) { content.Insert(position, text); }
public string Delete(int start, int end) { string deleted = content.ToString().Substring(start, end - start); content.Remove(start, end - start); return deleted; }
public string GetContent() { return content.ToString(); }}
public class InsertCommand : EditorCommand{ private Document document; private int position; private string text;
public InsertCommand(Document document, int position, string text) { this.document = document; this.position = position; this.text = text; }
public override void Execute() { document.Insert(position, text); }
public override void Undo() { document.Delete(position, position + text.Length); }
public override string Description => $"Insert '{text}' at {position}";}
public class TextEditor{ private Document document = new Document(); private Stack<EditorCommand> undoStack = new Stack<EditorCommand>(); private Stack<EditorCommand> redoStack = new Stack<EditorCommand>();
public void ExecuteCommand(EditorCommand command) { command.Execute(); undoStack.Push(command); redoStack.Clear(); Console.WriteLine($"✅ Executed: {command.Description}"); }
public bool Undo() { if (undoStack.Count == 0) { Console.WriteLine("⚠️ Nothing to undo"); return false; } EditorCommand command = undoStack.Pop(); command.Undo(); redoStack.Push(command); Console.WriteLine($"⏪ Undone: {command.Description}"); return true; }
public bool Redo() { if (redoStack.Count == 0) { Console.WriteLine("⚠️ Nothing to redo"); return false; } EditorCommand command = redoStack.Pop(); command.Execute(); undoStack.Push(command); Console.WriteLine($"⏩ Redone: {command.Description}"); return true; }
public Document GetDocument() => document;}
// Usageclass Program{ static void Main() { TextEditor editor = new TextEditor(); editor.ExecuteCommand(new InsertCommand(editor.GetDocument(), 0, "Hello")); editor.Undo(); editor.Redo(); Console.WriteLine("\n✅ Command Pattern: Full undo/redo with command history!"); }}package main
import ( "fmt" "strings")
// EditorCommand interfacetype EditorCommand interface { Execute() Undo() Description() string}
// Document (Receiver)type Document struct{ content strings.Builder }
func (d *Document) Insert(pos int, text string) { s := d.content.String() d.content.Reset() d.content.WriteString(s[:pos] + text + s[pos:])}
func (d *Document) Delete(start, end int) string { s := d.content.String() deleted := s[start:end] d.content.Reset() d.content.WriteString(s[:start] + s[end:]) return deleted}
func (d *Document) Content() string { return d.content.String() }
// Concrete Command: InsertCommandtype InsertCommand struct { doc *Document position int text string}
func (c *InsertCommand) Execute() { c.doc.Insert(c.position, c.text) }func (c *InsertCommand) Undo() { c.doc.Delete(c.position, c.position+len(c.text)) }func (c *InsertCommand) Description() string { return fmt.Sprintf("Insert '%s' at %d", c.text, c.position) }
// TextEditor (Invoker)type TextEditor struct { document *Document undoStack []EditorCommand redoStack []EditorCommand}
func NewTextEditor() *TextEditor { return &TextEditor{document: &Document{}} }
func (e *TextEditor) ExecuteCommand(cmd EditorCommand) { cmd.Execute() e.undoStack = append(e.undoStack, cmd) e.redoStack = nil // Clear redo fmt.Printf("✅ Executed: %s\n", cmd.Description())}
func (e *TextEditor) Undo() bool { if len(e.undoStack) == 0 { fmt.Println("⚠️ Nothing to undo"); return false } cmd := e.undoStack[len(e.undoStack)-1] e.undoStack = e.undoStack[:len(e.undoStack)-1] cmd.Undo() e.redoStack = append(e.redoStack, cmd) fmt.Printf("⏪ Undone: %s\n", cmd.Description()) return true}
func (e *TextEditor) Redo() bool { if len(e.redoStack) == 0 { fmt.Println("⚠️ Nothing to redo"); return false } cmd := e.redoStack[len(e.redoStack)-1] e.redoStack = e.redoStack[:len(e.redoStack)-1] cmd.Execute() e.undoStack = append(e.undoStack, cmd) fmt.Printf("⏩ Redone: %s\n", cmd.Description()) return true}
func main() { editor := NewTextEditor() editor.ExecuteCommand(&InsertCommand{editor.document, 0, "Hello"}) editor.Undo() editor.Redo() fmt.Println("\n✅ Command Pattern: Full undo/redo with command history!")}// Class Structuretrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}Command Pattern Variants
Section titled “Command Pattern Variants”There are different ways to implement the Command Pattern:
1. Simple Command (No Undo)
Section titled “1. Simple Command (No Undo)”When undo isn’t needed:
# Simple Command - no undo neededfrom abc import ABC, abstractmethod
class Command(ABC): @abstractmethod def execute(self): pass
class PrintCommand(Command): def __init__(self, message: str): self.message = message
def execute(self): print(self.message)
# Usagecmd = PrintCommand("Hello!")cmd.execute()// Simple Command - no undo neededinterface Command { void execute();}
class PrintCommand implements Command { private String message;
public PrintCommand(String message) { this.message = message; }
@Override public void execute() { System.out.println(message); }}
// UsageCommand cmd = new PrintCommand("Hello!");cmd.execute();// Simple Command - no undo neededinterface Command { execute(): void;}
class PrintCommand implements Command { constructor(private message: string) {}
execute(): void { console.log(this.message); }}
// Usageconst cmd: Command = new PrintCommand("Hello!");cmd.execute();// Simple Command - no undo needed#include <iostream>#include <string>
class Command {public: virtual ~Command() = default; virtual void execute() = 0;};
class PrintCommand : public Command {private: std::string message;
public: PrintCommand(const std::string& message) : message(message) {}
void execute() override { std::cout << message << std::endl; }};
// Usageint main() { Command* cmd = new PrintCommand("Hello!"); cmd->execute(); delete cmd; return 0;}// Simple Command - no undo neededusing System;
public interface ICommand{ void Execute();}
public class PrintCommand : ICommand{ private string message;
public PrintCommand(string message) { this.message = message; }
public void Execute() { Console.WriteLine(message); }}
// Usageclass Program{ static void Main() { ICommand cmd = new PrintCommand("Hello!"); cmd.Execute(); }}// Simple Command - no undo neededtype Command interface{ Execute() }
type PrintCommand struct{ message string }
func (p *PrintCommand) Execute() { fmt.Println(p.message) }
// cmd := &PrintCommand{"Hello!"}// cmd.Execute()// 1. Simple Command No Undotrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}Pros: Simple, lightweight
Cons: No undo support
2. Command with Callback
Section titled “2. Command with Callback”Using callbacks for results:
# Command with callbackfrom typing import Callable, Any
class AsyncCommand: def __init__(self, action: Callable, callback: Callable[[Any], None]): self.action = action self.callback = callback
def execute(self): result = self.action() self.callback(result)
# Usagedef fetch_data(): return {"users": [1, 2, 3]}
def handle_result(data): print(f"Got data: {data}")
cmd = AsyncCommand(fetch_data, handle_result)cmd.execute()// Command with callbackimport java.util.function.Consumer;import java.util.function.Supplier;
class AsyncCommand<T> { private Supplier<T> action; private Consumer<T> callback;
public AsyncCommand(Supplier<T> action, Consumer<T> callback) { this.action = action; this.callback = callback; }
public void execute() { T result = action.get(); callback.accept(result); }}
// UsageAsyncCommand<String> cmd = new AsyncCommand<>( () -> "Hello from async!", result -> System.out.println("Got: " + result));cmd.execute();// Command with callbacktype Action<T> = () => T;type Callback<T> = (result: T) => void;
class AsyncCommand<T> { constructor( private action: Action<T>, private callback: Callback<T> ) {}
execute(): void { const result = this.action(); this.callback(result); }}
// Usageconst fetchData = (): string => "Data from API";const handleResult = (data: string) => console.log(`Got data: ${data}`);
const cmd = new AsyncCommand(fetchData, handleResult);cmd.execute();// Command with callback#include <iostream>#include <functional>#include <string>
template<typename T>class AsyncCommand {private: std::function<T()> action; std::function<void(T)> callback;
public: AsyncCommand(std::function<T()> action, std::function<void(T)> callback) : action(action), callback(callback) {}
void execute() { T result = action(); callback(result); }};
// Usageint main() { auto fetchData = []() { return std::string("Data from API"); }; auto handleResult = [](const std::string& data) { std::cout << "Got data: " << data << std::endl; };
AsyncCommand<std::string> cmd(fetchData, handleResult); cmd.execute(); return 0;}// Command with callbackusing System;
public class AsyncCommand<T>{ private Func<T> action; private Action<T> callback;
public AsyncCommand(Func<T> action, Action<T> callback) { this.action = action; this.callback = callback; }
public void Execute() { T result = action(); callback(result); }}
// Usageclass Program{ static void Main() { Func<string> fetchData = () => "Data from API"; Action<string> handleResult = (data) => Console.WriteLine($"Got data: {data}");
var cmd = new AsyncCommand<string>(fetchData, handleResult); cmd.Execute(); }}// Command with callbacktype CallbackCommand[T any] struct { action func() T callback func(T)}
func (c *CallbackCommand[T]) Execute() { result := c.action() c.callback(result)}
func main() { cmd := &CallbackCommand[string]{ action: func() string { return "Data from API" }, callback: func(data string) { fmt.Printf("Got data: %s\n", data) }, } cmd.Execute()}// 2. Command with Callbacktrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}Pros: Supports async operations
Cons: More complex
3. Command Queue
Section titled “3. Command Queue”Queuing commands for batch execution:
# Command Queue - batch executionfrom collections import dequefrom abc import ABC, abstractmethod
class Command(ABC): @abstractmethod def execute(self): pass
class CommandQueue: def __init__(self): self._queue = deque()
def add(self, command: Command): self._queue.append(command)
def execute_all(self): while self._queue: command = self._queue.popleft() command.execute()
# Usagequeue = CommandQueue()queue.add(PrintCommand("First"))queue.add(PrintCommand("Second"))queue.add(PrintCommand("Third"))queue.execute_all() # Executes all in order// Command Queue - batch executionimport java.util.*;
class CommandQueue { private Queue<Command> queue = new LinkedList<>();
public void add(Command command) { queue.add(command); }
public void executeAll() { while (!queue.isEmpty()) { Command command = queue.poll(); command.execute(); } }}
// UsageCommandQueue queue = new CommandQueue();queue.add(new PrintCommand("First"));queue.add(new PrintCommand("Second"));queue.add(new PrintCommand("Third"));queue.executeAll(); // Executes all in order// Command Queue - batch executioninterface Command { execute(): void;}
class PrintCommand implements Command { constructor(private message: string) {} execute(): void { console.log(this.message); }}
class CommandQueue { private queue: Command[] = [];
add(command: Command): void { this.queue.push(command); }
executeAll(): void { while (this.queue.length > 0) { const command = this.queue.shift()!; command.execute(); } }}
// Usageconst queue = new CommandQueue();queue.add(new PrintCommand("First"));queue.add(new PrintCommand("Second"));queue.add(new PrintCommand("Third"));queue.executeAll();// Command Queue - batch execution#include <iostream>#include <queue>#include <string>
class Command {public: virtual ~Command() = default; virtual void execute() = 0;};
class PrintCommand : public Command {private: std::string message;public: PrintCommand(const std::string& message) : message(message) {} void execute() override { std::cout << message << std::endl; }};
class CommandQueue {private: std::queue<Command*> queue;
public: void add(Command* command) { queue.push(command); }
void executeAll() { while (!queue.empty()) { Command* command = queue.front(); command->execute(); queue.pop(); delete command; } }};
// Usageint main() { CommandQueue queue; queue.add(new PrintCommand("First")); queue.add(new PrintCommand("Second")); queue.add(new PrintCommand("Third")); queue.executeAll(); return 0;}// Command Queue - batch executionusing System;using System.Collections.Generic;
public interface ICommand{ void Execute();}
public class PrintCommand : ICommand{ private string message; public PrintCommand(string message) { this.message = message; } public void Execute() { Console.WriteLine(message); }}
public class CommandQueue{ private Queue<ICommand> queue = new Queue<ICommand>();
public void Add(ICommand command) { queue.Enqueue(command); }
public void ExecuteAll() { while (queue.Count > 0) { ICommand command = queue.Dequeue(); command.Execute(); } }}
// Usageclass Program{ static void Main() { CommandQueue queue = new CommandQueue(); queue.Add(new PrintCommand("First")); queue.Add(new PrintCommand("Second")); queue.Add(new PrintCommand("Third")); queue.ExecuteAll(); }}// Command Queue - batch executiontype CommandI interface{ Execute() }
type CommandQueue struct{ queue []CommandI }
func (q *CommandQueue) Add(cmd CommandI) { q.queue = append(q.queue, cmd) }func (q *CommandQueue) ExecuteAll() { for _, cmd := range q.queue { cmd.Execute() } q.queue = nil}
func main() { q := &CommandQueue{} q.Add(&PrintCommand{"First"}) q.Add(&PrintCommand{"Second"}) q.Add(&PrintCommand{"Third"}) q.ExecuteAll()}// 3. Command Queuetrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}When to Use Command Pattern?
Section titled “When to Use Command Pattern?”Use Command Pattern when:
✅ You need undo/redo - Commands know how to reverse themselves
✅ You want to queue operations - Execute later or in sequence
✅ You need operation logging - Commands can be serialized and logged
✅ You want to decouple - Invoker doesn’t know about receiver
✅ You need transactional behavior - Rollback if something fails
When NOT to Use Command Pattern?
Section titled “When NOT to Use Command Pattern?”Don’t use Command Pattern when:
❌ Simple operations - Direct method calls are clearer
❌ No undo needed - Overhead isn’t justified
❌ No operation history - Don’t need logging or auditing
❌ Performance critical - Command objects add overhead
❌ Over-engineering - Don’t add complexity for simple cases
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Commands That Don’t Store State for Undo
Section titled “Mistake 1: Commands That Don’t Store State for Undo”# ❌ Bad: Command doesn't store state for undoclass BadDeleteCommand: def __init__(self, document, start, end): self.document = document self.start = start self.end = end # Missing: self.deleted_text!
def execute(self): self.document.delete(self.start, self.end)
def undo(self): # Problem: What text to restore? We don't know! pass
# ✅ Good: Command stores state for undoclass GoodDeleteCommand: def __init__(self, document, start, end): self.document = document self.start = start self.end = end self.deleted_text = "" # Will store deleted text
def execute(self): self.deleted_text = self.document.delete(self.start, self.end)
def undo(self): self.document.insert(self.start, self.deleted_text)// ❌ Bad: Command doesn't store state for undoclass BadDeleteCommand implements Command { private Document document; private int start, end; // Missing: deletedText!
@Override public void execute() { document.delete(start, end); }
@Override public void undo() { // Problem: What text to restore? We don't know! }}
// ✅ Good: Command stores state for undoclass GoodDeleteCommand implements Command { private Document document; private int start, end; private String deletedText = ""; // Will store deleted text
@Override public void execute() { deletedText = document.delete(start, end); }
@Override public void undo() { document.insert(start, deletedText); }}// ❌ Bad: Command doesn't store state for undoclass Document { delete(start: number, end: number): string { return "deleted text"; } insert(pos: number, text: string): void {}}
class BadDeleteCommand { constructor(private document: Document, private start: number, private end: number) {}
execute(): void { this.document.delete(this.start, this.end); }
undo(): void { // Problem: What text to restore? We don't know! }}
// ✅ Good: Command stores state for undoclass GoodDeleteCommand { private deletedText: string = "";
constructor(private document: Document, private start: number, private end: number) {}
execute(): void { this.deletedText = this.document.delete(this.start, this.end); }
undo(): void { this.document.insert(this.start, this.deletedText); }}// ❌ Bad: Command doesn't store state for undoclass Document {public: std::string deleteRange(int start, int end) { return "deleted text"; } void insert(int pos, const std::string& text) {}};
class BadDeleteCommand {private: Document* document; int start, end; // Missing: deletedText!
public: void execute() { document->deleteRange(start, end); }
void undo() { // Problem: What text to restore? We don't know! }};
// ✅ Good: Command stores state for undoclass GoodDeleteCommand {private: Document* document; int start, end; std::string deletedText; // Store for undo!
public: void execute() { deletedText = document->deleteRange(start, end); }
void undo() { document->insert(start, deletedText); }};// ❌ Bad: Command doesn't store state for undopublic class Document{ public string Delete(int start, int end) { return "deleted text"; } public void Insert(int pos, string text) {}}
public class BadDeleteCommand{ private Document document; private int start, end; // Missing: deletedText!
public void Execute() { document.Delete(start, end); }
public void Undo() { // Problem: What text to restore? We don't know! }}
// ✅ Good: Command stores state for undopublic class GoodDeleteCommand{ private Document document; private int start, end; private string deletedText = ""; // Store for undo!
public void Execute() { deletedText = document.Delete(start, end); }
public void Undo() { document.Insert(start, deletedText); }}// ❌ Bad: Command doesn't store state for undotype BadDeleteCommand struct { doc *Document start, end int // Missing: deletedText!}
func (c *BadDeleteCommand) Execute() { c.doc.Delete(c.start, c.end) }func (c *BadDeleteCommand) Undo() { /* Problem: what text to restore? */ }
// ✅ Good: Command stores state for undotype GoodDeleteCommand struct { doc *Document start, end int deletedText string // Store for undo!}
func (c *GoodDeleteCommand) Execute() { c.deletedText = c.doc.Delete(c.start, c.end) }func (c *GoodDeleteCommand) Undo() { c.doc.Insert(c.start, c.deletedText) }// Mistake 1: Commands That Don't Store State for Undostruct Button;impl Button { fn click_save(&self) { println!("Saving directly"); } fn click_print(&self) { println!("Printing directly"); }}Mistake 2: Commands That Modify External State
Section titled “Mistake 2: Commands That Modify External State”# ❌ Bad: Command modifies external/global stateglobal_counter = 0
class BadCommand: def execute(self): global global_counter global_counter += 1 # Bad: Modifying global state!
def undo(self): global global_counter global_counter -= 1 # Problem: Race conditions!
# ✅ Good: Command only modifies controlled receiverclass GoodCommand: def __init__(self, counter): self.counter = counter # Controlled receiver self.prev_value = 0
def execute(self): self.prev_value = self.counter.get_value() self.counter.increment()
def undo(self): self.counter.set_value(self.prev_value)// ❌ Bad: Command modifies external/global stateclass BadCommand implements Command { private static int globalCounter = 0; // Static/global state!
@Override public void execute() { globalCounter++; // Bad: Modifying global state! }
@Override public void undo() { globalCounter--; // Problem: Race conditions! }}
// ✅ Good: Command only modifies controlled receiverclass GoodCommand implements Command { private Counter counter; // Controlled receiver private int prevValue;
public GoodCommand(Counter counter) { this.counter = counter; }
@Override public void execute() { prevValue = counter.getValue(); counter.increment(); }
@Override public void undo() { counter.setValue(prevValue); }}// ❌ Bad: Command modifies external/global statelet globalCounter = 0;
class BadCommand { execute(): void { globalCounter++; // Bad: Modifying global state! }
undo(): void { globalCounter--; // Problem: Race conditions! }}
// ✅ Good: Command only modifies controlled receiverclass Counter { private value: number = 0; getValue(): number { return this.value; } setValue(value: number): void { this.value = value; } increment(): void { this.value++; }}
class GoodCommand { private prevValue: number = 0;
constructor(private counter: Counter) {}
execute(): void { this.prevValue = this.counter.getValue(); this.counter.increment(); }
undo(): void { this.counter.setValue(this.prevValue); }}// ❌ Bad: Command modifies external/global stateint globalCounter = 0;
class BadCommand {public: void execute() { globalCounter++; // Bad: Modifying global state! }
void undo() { globalCounter--; // Problem: Race conditions! }};
// ✅ Good: Command only modifies controlled receiverclass Counter {private: int value = 0;public: int getValue() const { return value; } void setValue(int value) { this->value = value; } void increment() { value++; }};
class GoodCommand {private: Counter* counter; int prevValue = 0;
public: GoodCommand(Counter* counter) : counter(counter) {}
void execute() { prevValue = counter->getValue(); counter->increment(); }
void undo() { counter->setValue(prevValue); }};// ❌ Bad: Command modifies external/global statepublic static class GlobalState{ public static int Counter = 0;}
public class BadCommand{ public void Execute() { GlobalState.Counter++; // Bad: Modifying global state! }
public void Undo() { GlobalState.Counter--; // Problem: Race conditions! }}
// ✅ Good: Command only modifies controlled receiverpublic class Counter{ private int value = 0; public int GetValue() => value; public void SetValue(int value) { this.value = value; } public void Increment() { value++; }}
public class GoodCommand{ private Counter counter; private int prevValue = 0;
public GoodCommand(Counter counter) { this.counter = counter; }
public void Execute() { prevValue = counter.GetValue(); counter.Increment(); }
public void Undo() { counter.SetValue(prevValue); }}// ❌ Bad: Command modifies global statevar globalCounter int
type BadCmd struct{}
func (b *BadCmd) Execute() { globalCounter++ } // Bad: modifying global state!func (b *BadCmd) Undo() { globalCounter-- } // Problem: race conditions!
// ✅ Good: Command only modifies controlled receivertype Counter struct{ value int }
func (c *Counter) Increment() { c.value++ }func (c *Counter) GetValue() int { return c.value }func (c *Counter) SetValue(v int) { c.value = v }
type GoodCmd struct { counter *Counter prevValue int}
func (g *GoodCmd) Execute() { g.prevValue = g.counter.GetValue(); g.counter.Increment() }func (g *GoodCmd) Undo() { g.counter.SetValue(g.prevValue) }// Mistake 2: Commands That Modify External Statestruct Button;impl Button { fn click_save(&self) { println!("Saving directly"); } fn click_print(&self) { println!("Printing directly"); }}Mistake 3: Not Clearing Redo Stack on New Command
Section titled “Mistake 3: Not Clearing Redo Stack on New Command”# ❌ Bad: Not clearing redo stackclass BadEditor: def __init__(self): self.undo_stack = [] self.redo_stack = []
def execute(self, command): command.execute() self.undo_stack.append(command) # Missing: self.redo_stack.clear()! # Problem: Redo after new command causes inconsistency!
# ✅ Good: Clear redo stack on new commandclass GoodEditor: def __init__(self): self.undo_stack = [] self.redo_stack = []
def execute(self, command): command.execute() self.undo_stack.append(command) self.redo_stack.clear() # Clear redo stack!// ❌ Bad: Not clearing redo stackclass BadEditor { private List<Command> undoStack = new ArrayList<>(); private List<Command> redoStack = new ArrayList<>();
public void execute(Command command) { command.execute(); undoStack.add(command); // Missing: redoStack.clear()! // Problem: Redo after new command causes inconsistency! }}
// ✅ Good: Clear redo stack on new commandclass GoodEditor { private List<Command> undoStack = new ArrayList<>(); private List<Command> redoStack = new ArrayList<>();
public void execute(Command command) { command.execute(); undoStack.add(command); redoStack.clear(); // Clear redo stack! }}// ❌ Bad: Not clearing redo stackclass BadEditor { private undoStack: Command[] = []; private redoStack: Command[] = [];
execute(command: Command): void { command.execute(); this.undoStack.push(command); // Missing: this.redoStack = []; // Problem: Redo after new command causes inconsistency! }}
// ✅ Good: Clear redo stack on new commandclass GoodEditor { private undoStack: Command[] = []; private redoStack: Command[] = [];
execute(command: Command): void { command.execute(); this.undoStack.push(command); this.redoStack = []; // Clear redo stack! }}// ❌ Bad: Not clearing redo stackclass BadEditor {private: std::vector<Command*> undoStack; std::vector<Command*> redoStack;
public: void execute(Command* command) { command->execute(); undoStack.push_back(command); // Missing: redoStack.clear()! // Problem: Redo after new command causes inconsistency! }};
// ✅ Good: Clear redo stack on new commandclass GoodEditor {private: std::vector<Command*> undoStack; std::vector<Command*> redoStack;
public: void execute(Command* command) { command->execute(); undoStack.push_back(command); redoStack.clear(); // Clear redo stack! }};// ❌ Bad: Not clearing redo stackpublic class BadEditor{ private List<ICommand> undoStack = new List<ICommand>(); private List<ICommand> redoStack = new List<ICommand>();
public void Execute(ICommand command) { command.Execute(); undoStack.Add(command); // Missing: redoStack.Clear(); // Problem: Redo after new command causes inconsistency! }}
// ✅ Good: Clear redo stack on new commandpublic class GoodEditor{ private List<ICommand> undoStack = new List<ICommand>(); private List<ICommand> redoStack = new List<ICommand>();
public void Execute(ICommand command) { command.Execute(); undoStack.Add(command); redoStack.Clear(); // Clear redo stack! }}// ❌ Bad: Not clearing redo stacktype BadEditor struct { undoStack []CommandI redoStack []CommandI}
func (e *BadEditor) Execute(cmd CommandI) { cmd.Execute() e.undoStack = append(e.undoStack, cmd) // Missing: e.redoStack = nil - causes inconsistency!}
// ✅ Good: Clear redo stack on new commandtype GoodEditor struct { undoStack []CommandI redoStack []CommandI}
func (e *GoodEditor) Execute(cmd CommandI) { cmd.Execute() e.undoStack = append(e.undoStack, cmd) e.redoStack = nil // Clear redo stack!}// Mistake 3: Not Clearing Redo Stack on New Commandstruct Button;impl Button { fn click_save(&self) { println!("Saving directly"); } fn click_print(&self) { println!("Printing directly"); }}Benefits of Command Pattern
Section titled “Benefits of Command Pattern”- Decoupling - Invoker doesn’t know about receiver
- Undo/Redo - Commands know how to reverse themselves
- Queueing - Commands can be queued for later execution
- Logging - Commands can be serialized and logged
- Transactions - Group commands for atomic execution
- Macro Commands - Combine multiple commands into one
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Command Pattern?
Section titled “What is Command Pattern?”Command Pattern is a behavioral design pattern that turns a request into a stand-alone object containing all information about the request. This lets you parameterize methods with different requests, delay or queue a request’s execution, and support undoable operations.
Why Use It?
Section titled “Why Use It?”- ✅ Undo/Redo - Commands know how to reverse
- ✅ Queueing - Execute commands later
- ✅ Logging - Track all operations
- ✅ Decoupling - Invoker doesn’t know receiver
- ✅ Macro commands - Combine operations
How It Works?
Section titled “How It Works?”- Define Command interface - execute() and undo() methods
- Create Concrete Commands - Each wraps a receiver action
- Create Receiver - The object that performs the action
- Create Invoker - Triggers commands, manages history
- Client - Creates commands and configures invoker
Key Components
Section titled “Key Components”Client → creates → Command → calls → Receiver ↓ Invoker → invokes → Command- Command - Interface with execute() and undo()
- Concrete Command - Wraps receiver and action
- Receiver - Performs the actual work
- Invoker - Triggers commands, stores history
- Client - Creates and configures commands
Simple Example
Section titled “Simple Example”from abc import ABC, abstractmethod
class Command(ABC): @abstractmethod def execute(self): pass
@abstractmethod def undo(self): pass
class LightOnCommand(Command): def __init__(self, light): self.light = light def execute(self): self.light.turn_on() def undo(self): self.light.turn_off()
class RemoteControl: def __init__(self): self.history = [] def execute(self, command): command.execute() self.history.append(command) def undo(self): if self.history: self.history.pop().undo()interface Command { void execute(); void undo();}
class LightOnCommand implements Command { private Light light; LightOnCommand(Light light) { this.light = light; } public void execute() { light.turnOn(); } public void undo() { light.turnOff(); }}
class RemoteControl { private Stack<Command> history = new Stack<>(); void execute(Command cmd) { cmd.execute(); history.push(cmd); } void undo() { if (!history.isEmpty()) history.pop().undo(); }}interface Command { execute(): void; undo(): void;}
class LightOnCommand implements Command { constructor(private light: Light) {} execute(): void { this.light.turnOn(); } undo(): void { this.light.turnOff(); }}
class RemoteControl { private history: Command[] = []; execute(cmd: Command): void { cmd.execute(); this.history.push(cmd); } undo(): void { if (this.history.length) this.history.pop()!.undo(); }}class Command {public: virtual void execute() = 0; virtual void undo() = 0;};
class LightOnCommand : public Command { Light* light;public: LightOnCommand(Light* l) : light(l) {} void execute() override { light->turnOn(); } void undo() override { light->turnOff(); }};
class RemoteControl { std::vector<Command*> history;public: void execute(Command* cmd) { cmd->execute(); history.push_back(cmd); } void undo() { if (!history.empty()) { history.back()->undo(); history.pop_back(); } }};interface ICommand { void Execute(); void Undo();}
class LightOnCommand : ICommand { private Light light; public LightOnCommand(Light l) => light = l; public void Execute() => light.TurnOn(); public void Undo() => light.TurnOff();}
class RemoteControl { private Stack<ICommand> history = new(); public void Execute(ICommand cmd) { cmd.Execute(); history.Push(cmd); } public void Undo() { if (history.Count > 0) history.Pop().Undo(); }}type Command interface { Execute(); Undo() }
type LightOn struct{ light *Light }func (c *LightOn) Execute() { c.light.TurnOn() }func (c *LightOn) Undo() { c.light.TurnOff() }
type Remote struct{ history []Command }func (r *Remote) Execute(cmd Command) { cmd.Execute(); r.history = append(r.history, cmd) }func (r *Remote) Undo() { if len(r.history) == 0 { return } cmd := r.history[len(r.history)-1] r.history = r.history[:len(r.history)-1] cmd.Undo()}// Simple Exampletrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}When to Use?
Section titled “When to Use?”✅ Need undo/redo functionality
✅ Need to queue operations
✅ Need to log all operations
✅ Need to decouple invoker from receiver
✅ Need transactional behavior
When NOT to Use?
Section titled “When NOT to Use?”❌ Simple operations
❌ No undo needed
❌ No operation history needed
❌ Over-engineering simple cases
Key Takeaways
Section titled “Key Takeaways”- Command Pattern = Operations as objects
- Command = Encapsulates action and undo
- Invoker = Triggers commands, manages history
- Receiver = Performs actual work
- Benefit = Undo, queueing, logging, decoupling
Common Pattern Structure
Section titled “Common Pattern Structure”from abc import ABC, abstractmethod
# 1. Command Interfaceclass Command(ABC): @abstractmethod def execute(self): pass @abstractmethod def undo(self): pass
# 2. Concrete Commandclass ConcreteCommand(Command): def __init__(self, receiver): self.receiver = receiver self.prev_state = None def execute(self): self.prev_state = self.receiver.get_state() self.receiver.action() def undo(self): self.receiver.set_state(self.prev_state)
# 3. Invokerclass Invoker: def __init__(self): self.history = [] def execute(self, command): command.execute() self.history.append(command) def undo(self): if self.history: self.history.pop().undo()// 1. Command Interfaceinterface Command { void execute(); void undo();}
// 2. Concrete Commandclass ConcreteCommand implements Command { private Receiver receiver; private Object prevState; ConcreteCommand(Receiver r) { receiver = r; } public void execute() { prevState = receiver.getState(); receiver.action(); } public void undo() { receiver.setState(prevState); }}
// 3. Invokerclass Invoker { private Stack<Command> history = new Stack<>(); void execute(Command cmd) { cmd.execute(); history.push(cmd); } void undo() { if (!history.isEmpty()) history.pop().undo(); }}// 1. Command Interfaceinterface Command { execute(): void; undo(): void;}
// 2. Concrete Commandclass ConcreteCommand implements Command { private prevState: any; constructor(private receiver: Receiver) {} execute(): void { this.prevState = this.receiver.getState(); this.receiver.action(); } undo(): void { this.receiver.setState(this.prevState); }}
// 3. Invokerclass Invoker { private history: Command[] = []; execute(cmd: Command): void { cmd.execute(); this.history.push(cmd); } undo(): void { if (this.history.length) this.history.pop()!.undo(); }}// 1. Command Interfaceclass Command {public: virtual void execute() = 0; virtual void undo() = 0;};
// 2. Concrete Commandclass ConcreteCommand : public Command { Receiver* receiver; void* prevState;public: ConcreteCommand(Receiver* r) : receiver(r) {} void execute() override { prevState = receiver->getState(); receiver->action(); } void undo() override { receiver->setState(prevState); }};
// 3. Invokerclass Invoker { std::vector<Command*> history;public: void execute(Command* cmd) { cmd->execute(); history.push_back(cmd); } void undo() { if (!history.empty()) { history.back()->undo(); history.pop_back(); } }};// 1. Command Interfaceinterface ICommand { void Execute(); void Undo();}
// 2. Concrete Commandclass ConcreteCommand : ICommand { private object prevState; private Receiver receiver; public ConcreteCommand(Receiver r) => receiver = r; public void Execute() { prevState = receiver.GetState(); receiver.Action(); } public void Undo() => receiver.SetState(prevState);}
// 3. Invokerclass Invoker { private Stack<ICommand> history = new(); public void Execute(ICommand cmd) { cmd.Execute(); history.Push(cmd); } public void Undo() { if (history.Count > 0) history.Pop().Undo(); }}// 1. Command interfacetype Command interface { Execute(); Undo() }// 2. Concrete Commandtype ConcreteCmd struct{ receiver *Receiver; prevState any }func (c *ConcreteCmd) Execute() { c.prevState = c.receiver.GetState(); c.receiver.Action() }func (c *ConcreteCmd) Undo() { c.receiver.SetState(c.prevState) }// 3. Invokertype Invoker struct{ history []Command }func (inv *Invoker) Execute(cmd Command) { cmd.Execute() inv.history = append(inv.history, cmd)}func (inv *Invoker) Undo() { if len(inv.history) == 0 { return } cmd := inv.history[len(inv.history)-1] inv.history = inv.history[:len(inv.history)-1] cmd.Undo()}// Common Pattern Structuretrait Command { fn execute(&mut self); fn undo(&mut self);}struct AddText { text: String,}impl Command for AddText { fn execute(&mut self) { println!("Add {}", self.text); } fn undo(&mut self) { println!("Remove {}", self.text); }}struct Invoker { history: Vec<Box<dyn Command>>,}Remember
Section titled “Remember”- Command Pattern encapsulates operations as objects
- It enables undo/redo by storing state
- It supports queueing and logging operations
- Use it when you need operation history
- Don’t use it for simple direct calls!
Interview Focus: Command Pattern
Section titled “Interview Focus: Command Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Command Pattern is a behavioral design pattern that encapsulates a request as an object. This allows you to parameterize clients with different requests, queue operations, log them, and support undoable operations. The command object contains all information needed to perform an action.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of encapsulation
- Indicates you can explain concepts clearly
2. When to Use Command Pattern
Section titled “2. When to Use Command Pattern”Must mention:
- ✅ Undo/Redo - Each command knows how to reverse itself
- ✅ Operation queueing - Execute later or in batch
- ✅ Logging/Auditing - Commands can be serialized
- ✅ Decoupling - Invoker doesn’t know about receiver
- ✅ Transactional behavior - Rollback on failure
Example scenario to give:
“I’d use Command Pattern when building a text editor. Each operation - typing, deleting, formatting - is a command object. This makes undo/redo trivial: just pop from undo stack, call undo(), push to redo stack. We can also log all commands for crash recovery or collaboration features.”
Must discuss:
- Command - Encapsulates an operation with its state, supports undo
- Strategy - Encapsulates an algorithm, algorithms are interchangeable
- Key difference - Command has state and undo, Strategy is stateless
Example to give:
“Command Pattern encapsulates an operation - ‘delete text at position 5’ - and knows how to undo it. Strategy Pattern encapsulates an algorithm - ‘sort using QuickSort’ - but doesn’t track state. Commands remember what they did so they can undo it; strategies just execute algorithms.”
4. Implementing Undo/Redo
Section titled “4. Implementing Undo/Redo”Must explain:
- Undo Stack - Stores executed commands
- Redo Stack - Stores undone commands
- Execute - Execute command, push to undo stack, clear redo stack
- Undo - Pop from undo, call undo(), push to redo
- Redo - Pop from redo, call execute(), push to undo
Code to have ready:
class Editor: def __init__(self): self.undo_stack = [] self.redo_stack = []
def execute(self, command): command.execute() self.undo_stack.append(command) self.redo_stack.clear() # Important!
def undo(self): if self.undo_stack: cmd = self.undo_stack.pop() cmd.undo() self.redo_stack.append(cmd)
def redo(self): if self.redo_stack: cmd = self.redo_stack.pop() cmd.execute() self.undo_stack.append(cmd)5. Benefits and Trade-offs
Section titled “5. Benefits and Trade-offs”Benefits to mention:
- Decoupling - Invoker doesn’t know about receiver
- Undo/Redo - Commands are reversible
- Queueing - Commands can be queued
- Logging - Commands can be serialized
- Macro Commands - Combine multiple commands
Trade-offs to acknowledge:
- Complexity - More classes than direct calls
- Memory - Commands store state for undo
- Overhead - Command objects have cost
- Overkill for simple cases - Direct calls are simpler
6. Common Interview Questions
Section titled “6. Common Interview Questions”Q: “How would you implement undo in a text editor?”
A:
“I’d use Command Pattern. Each operation - TypeCommand, DeleteCommand, FormatCommand - stores the state needed to undo itself. DeleteCommand stores the deleted text. When execute() is called, it deletes and stores what was deleted. When undo() is called, it restores the deleted text. The editor maintains undo/redo stacks to track command history.”
Q: “How does Command Pattern support transactions?”
A:
“You can create a TransactionCommand that holds multiple commands. On execute(), it executes all commands in sequence. If any fails, it calls undo() on all previously executed commands in reverse order. This gives atomic, all-or-nothing behavior - either all commands succeed or all are rolled back.”
Q: “How does Command Pattern relate to SOLID principles?”
A:
“Command Pattern supports Single Responsibility - each command handles one operation. It supports Open/Closed - add new commands without modifying invoker. It supports Dependency Inversion - invoker depends on Command interface, not concrete commands. It also supports Interface Segregation - Command interface is focused (execute, undo).”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Command Pattern clearly in one sentence
- Explain when to use it (undo, queueing, logging)
- Describe the structure: Command, Invoker, Receiver
- Implement undo/redo from scratch
- Compare with Strategy Pattern
- List benefits and trade-offs
- Connect to SOLID principles
- Identify when NOT to use it
- Give 2-3 real-world examples (editors, transactions, GUI)
- Discuss Macro Commands (composite)
Remember: Command Pattern is about encapsulating operations as objects - enabling undo, queueing, and logging with full control over execution! 🎮