Command, Chain of Responsibility and Template Method
Behavioral patterns manage communication and responsibilities between objects. Command encapsulates requests as objects (undo/redo), Chain of Responsibility passes requests along a chain of handlers (middleware), and Template Method defines algorithm skeletons with customizable steps.
🎯 What You Will Learn
- Command pattern: encapsulating actions as objects
- Implementing undo/redo with Command
- Chain of Responsibility: chain of handlers
- Template Method: algorithms with hook points
Command Pattern
The Command pattern encapsulates a request as an object, allowing you to parameterize methods with operations, queue requests, and support undoable operations (undo/redo).
// Command interface
interface Command {{ '{' }}
execute(): void;
undo(): void;
{{ '}' }}
// Receiver: the object that performs the actual action
class Light {{ '{' }}
private isOn: boolean = false;
turnOn(): void {{ '{' }}
this.isOn = true;
console.log("💡 Light turned on");
{{ '}' }}
turnOff(): void {{ '{' }}
this.isOn = false;
console.log("🌑 Light turned off");
{{ '}' }}
getState(): boolean {{ '{' }}
return this.isOn;
{{ '}' }}
{{ '}' }}
// Concrete Commands
class TurnOnCommand implements Command {{ '{' }}
constructor(private light: Light) {{ '{' }}{{ '}' }}
execute(): void {{ '{' }}
this.light.turnOn();
{{ '}' }}
undo(): void {{ '{' }}
this.light.turnOff();
{{ '}' }}
{{ '}' }}
class TurnOffCommand implements Command {{ '{' }}
constructor(private light: Light) {{ '{' }}{{ '}' }}
execute(): void {{ '{' }}
this.light.turnOff();
{{ '}' }}
undo(): void {{ '{' }}
this.light.turnOn();
{{ '}' }}
{{ '}' }}
// Invoker: executes commands
class RemoteControl {{ '{' }}
private history: Command[] = [];
executeCommand(command: Command): void {{ '{' }}
command.execute();
this.history.push(command);
{{ '}' }}
undo(): void {{ '{' }}
const command = this.history.pop();
if (command) {{ '{' }}
command.undo();
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Usage
const light = new Light();
const remote = new RemoteControl();
remote.executeCommand(new TurnOnCommand(light)); // "💡 Light turned on"
remote.executeCommand(new TurnOffCommand(light)); // "🌑 Light turned off"
remote.undo(); // "💡 Light turned on" (undoes the turn off)
Command with Full Undo/Redo
Advanced implementation with multiple undo/redo operations:
class TextEditor {{ '{' }}
private content: string = "";
getContent(): string {{ '{' }}
return this.content;
{{ '}' }}
setContent(content: string): void {{ '{' }}
this.content = content;
{{ '}' }}
append(text: string): void {{ '{' }}
this.content += text;
{{ '}' }}
delete(length: number): void {{ '{' }}
this.content = this.content.slice(0, -length);
{{ '}' }}
{{ '}' }}
class AppendCommand implements Command {{ '{' }}
constructor(
private editor: TextEditor,
private text: string
) {{ '{' }}{{ '}' }}
execute(): void {{ '{' }}
this.editor.append(this.text);
{{ '}' }}
undo(): void {{ '{' }}
this.editor.delete(this.text.length);
{{ '}' }}
{{ '}' }}
class DeleteCommand implements Command {{ '{' }}
private deletedText: string = "";
constructor(
private editor: TextEditor,
private length: number
) {{ '{' }}{{ '}' }}
execute(): void {{ '{' }}
const content = this.editor.getContent();
this.deletedText = content.slice(-this.length);
this.editor.delete(this.length);
{{ '}' }}
undo(): void {{ '{' }}
this.editor.append(this.deletedText);
{{ '}' }}
{{ '}' }}
class CommandManager {{ '{' }}
private history: Command[] = [];
private redoStack: Command[] = [];
execute(command: Command): void {{ '{' }}
command.execute();
this.history.push(command);
this.redoStack = []; // Clear redo stack on new action
{{ '}' }}
undo(): void {{ '{' }}
const command = this.history.pop();
if (command) {{ '{' }}
command.undo();
this.redoStack.push(command);
{{ '}' }}
{{ '}' }}
redo(): void {{ '{' }}
const command = this.redoStack.pop();
if (command) {{ '{' }}
command.execute();
this.history.push(command);
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Usage
const editor = new TextEditor();
const manager = new CommandManager();
manager.execute(new AppendCommand(editor, "Hello "));
manager.execute(new AppendCommand(editor, "World"));
console.log(editor.getContent()); // "Hello World"
manager.undo();
console.log(editor.getContent()); // "Hello "
manager.redo();
console.log(editor.getContent()); // "Hello World"
Chain of Responsibility Pattern
The Chain of Responsibility allows multiple objects to handle a request without the sender knowing which one will handle it. The request passes along a chain until someone handles it.
// Abstract handler
abstract class Handler {{ '{' }}
protected next: Handler | null = null;
setNext(handler: Handler): Handler {{ '{' }}
this.next = handler;
return handler; // Fluent interface
{{ '}' }}
handle(request: string): void {{ '{' }}
if (this.canHandle(request)) {{ '{' }}
this.process(request);
{{ '}' }} else if (this.next) {{ '{' }}
this.next.handle(request);
{{ '}' }} else {{ '{' }}
console.log(`❌ No handler for: ${{ '{' }}request{{ '}' }}`);
{{ '}' }}
{{ '}' }}
protected abstract canHandle(request: string): boolean;
protected abstract process(request: string): void;
{{ '}' }}
// Concrete Handlers
class SupportLevel1 extends Handler {{ '{' }}
protected canHandle(request: string): boolean {{ '{' }}
return request.includes("password");
{{ '}' }}
protected process(request: string): void {{ '{' }}
console.log("👤 Level 1: Password reset sent");
{{ '}' }}
{{ '}' }}
class SupportLevel2 extends Handler {{ '{' }}
protected canHandle(request: string): boolean {{ '{' }}
return request.includes("bug");
{{ '}' }}
protected process(request: string): void {{ '{' }}
console.log("🔧 Level 2: Bug investigation started");
{{ '}' }}
{{ '}' }}
class SupportLevel3 extends Handler {{ '{' }}
protected canHandle(request: string): boolean {{ '{' }}
return request.includes("crash");
{{ '}' }}
protected process(request: string): void {{ '{' }}
console.log("🚨 Level 3: Critical crash escalated to engineering");
{{ '}' }}
{{ '}' }}
// Chain setup
const level1 = new SupportLevel1();
const level2 = new SupportLevel2();
const level3 = new SupportLevel3();
level1.setNext(level2).setNext(level3);
// Usage
level1.handle("I forgot my password"); // "👤 Level 1: Password reset sent"
level1.handle("There is a bug in the form"); // "🔧 Level 2: Bug investigation started"
level1.handle("The app crashes"); // "🚨 Level 3: Critical crash escalated to engineering"
level1.handle("Generic question"); // "❌ No handler for: Generic question"
Chain of Responsibility: HTTP Middleware
Practical example: middleware pipeline for handling HTTP requests:
interface Request {{ '{' }}
url: string;
method: string;
headers: Record<string, string>;
body?: any;
{{ '}' }}
type NextFunction = () => void;
abstract class Middleware {{ '{' }}
protected next: Middleware | null = null;
setNext(middleware: Middleware): Middleware {{ '{' }}
this.next = middleware;
return middleware;
{{ '}' }}
handle(req: Request, next: NextFunction): void {{ '{' }}
this.process(req, () => {{ '{' }}
if (this.next) {{ '{' }}
this.next.handle(req, next);
{{ '}' }} else {{ '{' }}
next();
{{ '}' }}
{{ '}' }});
{{ '}' }}
protected abstract process(req: Request, next: NextFunction): void;
{{ '}' }}
class AuthMiddleware extends Middleware {{ '{' }}
protected process(req: Request, next: NextFunction): void {{ '{' }}
console.log("🔐 Checking authentication...");
if (req.headers['authorization']) {{ '{' }}
console.log("✅ Authenticated");
next();
{{ '}' }} else {{ '{' }}
console.log("❌ Unauthorized - blocking request");
{{ '}' }}
{{ '}' }}
{{ '}' }}
class LoggingMiddleware extends Middleware {{ '{' }}
protected process(req: Request, next: NextFunction): void {{ '{' }}
console.log(`📝 ${{ '{' }}req.method{{ '}' }} ${{ '{' }}req.url{{ '}' }}`);
next();
{{ '}' }}
{{ '}' }}
class ValidationMiddleware extends Middleware {{ '{' }}
protected process(req: Request, next: NextFunction): void {{ '{' }}
console.log("✔️ Validating request...");
if (req.method === 'POST' && !req.body) {{ '{' }}
console.log("❌ Validation failed: missing body");
{{ '}' }} else {{ '{' }}
console.log("✅ Validation passed");
next();
{{ '}' }}
{{ '}' }}
{{ '}' }}
// Setup middleware chain
const auth = new AuthMiddleware();
const logging = new LoggingMiddleware();
const validation = new ValidationMiddleware();
auth.setNext(logging).setNext(validation);
// Usage
const request: Request = {{ '{' }}
url: '/api/users',
method: 'POST',
headers: {{ '{' }} authorization: 'Bearer token123' {{ '}' }},
body: {{ '{' }} name: 'Alice' {{ '}' }}
{{ '}' }};
auth.handle(request, () => console.log("🎯 Request processed!"));
// Output:
// 🔐 Checking authentication...
// ✅ Authenticated
// 📝 POST /api/users
// ✔️ Validating request...
// ✅ Validation passed
// 🎯 Request processed!
Template Method Pattern
The Template Method defines the skeleton of an algorithm in a base class, delegating certain steps to subclasses. Subclasses redefine specific steps without changing the overall structure.
abstract class DataProcessor {{ '{' }}
// Template method: defines the algorithm
public process(): void {{ '{' }}
this.readData();
this.processData();
this.saveData();
this.sendNotification(); // Optional hook
{{ '}' }}
// Abstract steps (to be implemented)
protected abstract readData(): void;
protected abstract processData(): void;
protected abstract saveData(): void;
// Hook: method with default implementation
protected sendNotification(): void {{ '{' }}
// Default: does nothing (subclasses can override)
{{ '}' }}
{{ '}' }}
// Concrete implementation
class CSVProcessor extends DataProcessor {{ '{' }}
protected readData(): void {{ '{' }}
console.log("📄 Reading CSV file...");
{{ '}' }}
protected processData(): void {{ '{' }}
console.log("🔄 Parsing CSV rows...");
{{ '}' }}
protected saveData(): void {{ '{' }}
console.log("💾 Saving to database...");
{{ '}' }}
protected sendNotification(): void {{ '{' }}
console.log("📧 Email notification sent");
{{ '}' }}
{{ '}' }}
class JSONProcessor extends DataProcessor {{ '{' }}
protected readData(): void {{ '{' }}
console.log("📄 Reading JSON file...");
{{ '}' }}
protected processData(): void {{ '{' }}
console.log("🔄 Parsing JSON objects...");
{{ '}' }}
protected saveData(): void {{ '{' }}
console.log("💾 Saving to cache...");
{{ '}' }}
// Does not override sendNotification (uses default)
{{ '}' }}
// Usage
const csvProcessor = new CSVProcessor();
csvProcessor.process();
// 📄 Reading CSV file...
// 🔄 Parsing CSV rows...
// 💾 Saving to database...
// 📧 Email notification sent
const jsonProcessor = new JSONProcessor();
jsonProcessor.process();
// 📄 Reading JSON file...
// 🔄 Parsing JSON objects...
// 💾 Saving to cache...
// (no notification)
Advanced Template Method: Test Framework
Realistic example: automated test framework with setup/teardown:
abstract class TestCase {{ '{' }}
// Template method
public run(): void {{ '{' }}
this.setUp();
try {{ '{' }}
this.runTest();
console.log("✅ Test passed");
{{ '}' }} catch (error) {{ '{' }}
console.log(`❌ Test failed: ${{ '{' }}error{{ '}' }}`);
{{ '}' }} finally {{ '{' }}
this.tearDown();
{{ '}' }}
{{ '}' }}
// Hook methods
protected setUp(): void {{ '{' }}
console.log("🔧 Setting up test...");
{{ '}' }}
protected tearDown(): void {{ '{' }}
console.log("🧹 Cleaning up...");
{{ '}' }}
// Abstract step: each test defines its own
protected abstract runTest(): void;
{{ '}' }}
// Concrete tests
class DatabaseTest extends TestCase {{ '{' }}
private connection: any;
protected setUp(): void {{ '{' }}
super.setUp();
console.log("🔌 Connecting to test database...");
this.connection = {{ '{' }} connected: true {{ '}' }};
{{ '}' }}
protected runTest(): void {{ '{' }}
console.log("🧪 Testing database queries...");
if (!this.connection.connected) {{ '{' }}
throw new Error("Connection failed");
{{ '}' }}
{{ '}' }}
protected tearDown(): void {{ '{' }}
console.log("🔌 Closing database connection...");
this.connection = null;
super.tearDown();
{{ '}' }}
{{ '}' }}
class APITest extends TestCase {{ '{' }}
protected setUp(): void {{ '{' }}
super.setUp();
console.log("🌐 Starting mock API server...");
{{ '}' }}
protected runTest(): void {{ '{' }}
console.log("🧪 Testing API endpoints...");
// Test logic here
{{ '}' }}
protected tearDown(): void {{ '{' }}
console.log("🛑 Stopping mock server...");
super.tearDown();
{{ '}' }}
{{ '}' }}
// Usage
new DatabaseTest().run();
// 🔧 Setting up test...
// 🔌 Connecting to test database...
// 🧪 Testing database queries...
// ✅ Test passed
// 🔌 Closing database connection...
// 🧹 Cleaning up...
new APITest().run();
// 🔧 Setting up test...
// 🌐 Starting mock API server...
// 🧪 Testing API endpoints...
// ✅ Test passed
// 🛑 Stopping mock server...
// 🧹 Cleaning up...
When to Use Each Pattern
✅ Use Command when:
- You need undo/redo in applications
- You want to queue operations (job queue)
- You need to parameterize objects with actions
- Logging and transactions: recording executed commands
✅ Use Chain of Responsibility when:
- Multiple objects can handle a request, but you don't know which one
- You want middleware/pipeline for sequential processing
- The set of handlers can change dynamically
- Event handling, logging, authentication
✅ Use Template Method when:
- You have algorithms with a common structure but different steps
- You want to avoid duplication of similar code
- You need control over which steps subclasses can override
- Frameworks: define the skeleton, users implement details
Practical Example: Payment System
// COMMAND: Transactions with undo
interface PaymentCommand {{ '{' }}
execute(): void;
undo(): void;
{{ '}' }}
class ChargeCommand implements PaymentCommand {{ '{' }}
constructor(
private account: string,
private amount: number
) {{ '{' }}{{ '}' }}
execute(): void {{ '{' }}
console.log(`💳 Charged ${{ '{' }}this.amount{{ '}' }} to ${{ '{' }}this.account{{ '}' }}`);
{{ '}' }}
undo(): void {{ '{' }}
console.log(`↩️ Refunded ${{ '{' }}this.amount{{ '}' }} to ${{ '{' }}this.account{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// CHAIN: Payment validation
abstract class PaymentHandler {{ '{' }}
protected next: PaymentHandler | null = null;
setNext(handler: PaymentHandler): PaymentHandler {{ '{' }}
this.next = handler;
return handler;
{{ '}' }}
handle(amount: number): boolean {{ '{' }}
if (this.validate(amount)) {{ '{' }}
return this.next ? this.next.handle(amount) : true;
{{ '}' }}
return false;
{{ '}' }}
protected abstract validate(amount: number): boolean;
{{ '}' }}
class AmountValidator extends PaymentHandler {{ '{' }}
protected validate(amount: number): boolean {{ '{' }}
if (amount > 0) {{ '{' }}
console.log("✅ Amount valid");
return true;
{{ '}' }}
console.log("❌ Invalid amount");
return false;
{{ '}' }}
{{ '}' }}
class FraudDetector extends PaymentHandler {{ '{' }}
protected validate(amount: number): boolean {{ '{' }}
if (amount < 10000) {{ '{' }}
console.log("✅ No fraud detected");
return true;
{{ '}' }}
console.log("🚨 Potential fraud");
return false;
{{ '}' }}
{{ '}' }}
// TEMPLATE METHOD: Payment processing
abstract class PaymentProcessor {{ '{' }}
public processPayment(amount: number): void {{ '{' }}
if (!this.validatePayment(amount)) return;
this.deductAmount(amount);
this.sendReceipt();
{{ '}' }}
protected abstract validatePayment(amount: number): boolean;
protected abstract deductAmount(amount: number): void;
protected sendReceipt(): void {{ '{' }}
console.log("📧 Receipt sent");
{{ '}' }}
{{ '}' }}
class CreditCardProcessor extends PaymentProcessor {{ '{' }}
private validator = new AmountValidator();
constructor() {{ '{' }}
super();
this.validator.setNext(new FraudDetector());
{{ '}' }}
protected validatePayment(amount: number): boolean {{ '{' }}
return this.validator.handle(amount);
{{ '}' }}
protected deductAmount(amount: number): void {{ '{' }}
console.log(`💳 Card charged ${{ '{' }}amount{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Combined usage
const processor = new CreditCardProcessor();
processor.processPayment(500);
// ✅ Amount valid
// ✅ No fraud detected
// 💳 Card charged 500
// 📧 Receipt sent
Conclusion
These three behavioral patterns solve different but complementary problems. Command encapsulates actions for undo/redo and queuing, Chain of Responsibility creates flexible handler pipelines, and Template Method reduces duplication with reusable algorithms. Combining them creates elegant and maintainable architectures.
Related deep dive: 05 - Observer e Strategy Pattern: Comportamento Flessibile.
Related deep dive: 1 - Clean Architecture in Angular: Struttura Scalabile.
🎯 Key Takeaways
- Command = requests as objects, undo/redo, job queues
- Chain of Responsibility = chain of handlers, middleware
- Template Method = algorithm skeleton, hook points
- Use Command for undoable operations
- Use Chain for processing pipelines
- Use Template Method to avoid duplication
Frequently asked questions
What is the purpose of the Command pattern?
The Command pattern encapsulates a request as an object, allowing you to parameterize methods with operations, queue requests, and support undoable operations (undo/redo).
How does the Chain of Responsibility pattern handle requests?
The Chain of Responsibility allows multiple objects to handle a request without the sender knowing which one will handle it. The request passes along a chain until someone handles it.
What is the main benefit of using the Template Method pattern?
The Template Method defines the skeleton of an algorithm in a base class, delegating certain steps to subclasses. Subclasses redefine specific steps without changing the overall structure.
How do these three behavioral patterns complement each other?
These three behavioral patterns solve different but complementary problems. Command encapsulates actions for undo/redo and queuing, Chain of Responsibility creates flexible handler pipelines, and Template Method reduces duplication with reusable algorithms.
Commenti
Caricamento commenti...
Sign in to comment