Strategy

Strategy Pattern

Introduction

The Strategy pattern is a behavioral design pattern that enables selecting an algorithm’s behavior at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

Purpose

  • To define a family of algorithms, encapsulate each one, and make them interchangeable.
  • To let the algorithm vary independently from clients that use it.

Use Cases

  • When there are many related classes that only differ in their behavior.
  • For algorithms used in a class defined in many subclasses, providing an alternative to conditional statements.
  • To manage the complexities of algorithms used and the clarity of the overall system.

Advantages/Disadvantages

Advantages:

  • A family of related algorithms can be defined and maintained in each derived class.
  • Provides an alternative to conditional statements for selecting desired behavior.
  • Supports the Open/Closed Principle.

Disadvantages:

  • The number of objects increases as new strategies are introduced.
  • Clients must be aware of the differences between strategies to select the right one.

Implementation in JavaScript

javascript
	class Strategy {
	  execute() {}
	}
	
	class ConcreteStrategyA extends Strategy {
	  execute() {
	    console.log('Implementing Strategy A');
	  }
	}
	
	class ConcreteStrategyB extends Strategy {
	  execute() {
	    console.log('Implementing Strategy B');
	  }
	}
	
	class Context {
	  constructor(strategy) {
	    this.strategy = strategy;
	  }
	
	  setStrategy(strategy) {
	    this.strategy = strategy;
	  }
	
	  executeStrategy() {
	    this.strategy.execute();
	  }
	}
	
	// Usage
	const strategyA = new ConcreteStrategyA();
	const strategyB = new ConcreteStrategyB();
	const context = new Context(strategyA);
	
	context.executeStrategy(); // Implementing Strategy A
	context.setStrategy(strategyB);
	context.executeStrategy(); // Implementing Strategy B

Practical Example

Consider a backend application where you need different logging strategies (e.g., logging to console, logging to a file) based on the runtime environment or user preference.

javascript
	class LoggingStrategy {
	  log(message) {}
	}
	
	class ConsoleLoggingStrategy extends LoggingStrategy {
	  log(message) {
	    console.log(`Console: ${message}`);
	  }
	}
	
	class FileLoggingStrategy extends LoggingStrategy {
	  log(message) {
	    // Simulate file logging
	    console.log(`File: ${message}`);
	  }
	}
	
	class Logger {
	  constructor(strategy) {
	    this.strategy = strategy;
	  }
	
	  setStrategy(strategy) {
	    this.strategy = strategy;
	  }
	
	  log(message) {
	    this.strategy.log(message);
	  }
	}
	
	// Usage
	const consoleStrategy = new ConsoleLoggingStrategy();
	const fileStrategy = new FileLoggingStrategy();
	const logger = new Logger(consoleStrategy);
	
	logger.log('Logging to console'); // Logs to console
	logger.setStrategy(fileStrategy);
	logger.log('Logging to file'); // Simulates logging to a file

In this example, Logger uses different LoggingStrategy objects (like ConsoleLoggingStrategy and FileLoggingStrategy) to change the logging behavior dynamically. This design allows easy swapping of the logging method without altering the logger class.