165 – Strategy pattern (Javascript)

Design Patterns in JavaScript – Strategy Pattern

The Strategy pattern is a behavioral design pattern in JavaScript that defines a family of interchangeable algorithms and allows them to be used interchangeably within an object. This pattern promotes code flexibility and reusability, making it a valuable asset in JavaScript application development. In this guide, we’ll explore the Strategy pattern, its purpose, and how to implement it in JavaScript.

Understanding the Strategy Pattern

The Strategy pattern is based on the concept of encapsulating a set of related algorithms or behaviors into separate classes. These classes are referred to as strategies, and they can be used interchangeably by an object that needs to perform a specific task.

Key components of the Strategy pattern include:

  • Context: This is the object that holds a reference to a strategy and can switch between different strategies dynamically.
  • Strategy: An interface or abstract class that defines a set of methods that concrete strategies must implement.
  • Concrete Strategies: These are the classes that implement specific algorithms or behaviors as per the strategy interface.

The Strategy pattern separates the responsibility for a particular task into distinct classes, promoting the open/closed principle, which states that code should be open for extension but closed for modification.

Advantages of the Strategy Pattern

Using the Strategy pattern in your JavaScript code offers several advantages:

  • Flexibility: You can easily swap or change strategies at runtime, adapting to different requirements.
  • Code Reusability: Strategies are reusable across different contexts and objects.
  • Maintainability: As each strategy is encapsulated in a separate class, changes to one strategy don’t affect others.
Implementing the Strategy Pattern in JavaScript

Let’s see how to implement a basic Strategy pattern in JavaScript:

Example of the Strategy Pattern

// Strategy interface
class PaymentStrategy {
  pay(amount) {}
}

// Concrete Strategies
class CreditCardPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid $${amount} with Credit Card.`);
  }
}

class PayPalPayment extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid $${amount} with PayPal.`);
  }
}

// Context
class ShoppingCart {
  constructor() {
    this.totalAmount = 0;
    this.paymentStrategy = null;
  }

  setPaymentStrategy(strategy) {
    this.paymentStrategy = strategy;
  }

  checkout() {
    if (this.paymentStrategy) {
      this.paymentStrategy.pay(this.totalAmount);
    } else {
      console.log('Please select a payment method before checkout.');
    }
  }

  addItem(itemPrice) {
    this.totalAmount += itemPrice;
  }
}

// Usage
const cart = new ShoppingCart();
cart.addItem(50);
cart.addItem(30);

const creditCardPayment = new CreditCardPayment();
cart.setPaymentStrategy(creditCardPayment);
cart.checkout(); // Output: Paid $80 with Credit Card

const payPalPayment = new PayPalPayment();
cart.setPaymentStrategy(payPalPayment);
cart.checkout(); // Output: Paid $80 with PayPal

In this example, we have a `PaymentStrategy` interface, two concrete strategies (`CreditCardPayment` and `PayPalPayment`), and a `ShoppingCart` context. The context can set and change payment strategies and perform checkout using the selected strategy.

Use Cases for the Strategy Pattern

The Strategy pattern is useful in various scenarios, including:

  • Sorting Algorithms: Different sorting algorithms can be implemented as separate strategies and selected dynamically.
  • Payment Gateways: Choosing different payment methods during an e-commerce checkout process.
  • Text Editors: Implementing various text formatting strategies.
Potential Drawbacks

The Strategy pattern can introduce additional complexity to your codebase, especially when there are many strategies involved. It’s essential to strike a balance between code flexibility and maintainability.

Conclusion

The Strategy pattern is a valuable tool for designing flexible and maintainable JavaScript applications. It allows you to encapsulate algorithms and behaviors into interchangeable classes, promoting code reusability and adaptability. By applying the Strategy pattern, you can make your codebase more robust and easier to maintain.