Strategy Design Pattern in JavaScript

1. Definition

The Strategy Design Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

2. Problem Statement

Consider an application that needs to provide multiple ways of doing a specific task, like processing payments. Different types of payments (e.g., credit card, PayPal, bank transfer) require different algorithms or strategies. Implementing these strategies directly in the main application makes it hard to maintain, extend, and is against the Open-Closed Principle.

3. Solution

Separate the varying algorithms or strategies into their own classes. Create an interface that each strategy will implement, and the main application will use this interface to communicate with the strategies. This decouples the strategies from the main application and provides flexibility.

4. Real-World Use Cases

1. Payment processing systems where there are multiple payment methods.

2. Compression algorithms (ZIP, TAR, RAR) in file archiving applications.

3. Sorting algorithms used interchangeably based on the data.

5. Implementation Steps

1. Define a Strategy interface that encapsulates the interchangeable algorithms.

2. Implement multiple concrete classes for each algorithm or strategy.

3. Create a context class that uses the Strategy interface to communicate and delegate tasks to the encapsulated strategy.

6. Implementation in JavaScript

// Strategy Interface
class PaymentStrategy {
    processPayment(amount) {
        // to be overridden by concrete strategies
    }
}
// Concrete Strategies
class CreditCardStrategy extends PaymentStrategy {
    processPayment(amount) {
        return `Processed payment of $${amount} using Credit Card.`;
    }
}
class PayPalStrategy extends PaymentStrategy {
    processPayment(amount) {
        return `Processed payment of $${amount} using PayPal.`;
    }
}
// Context Class
class PaymentProcessor {
    constructor(strategy) {
        this.strategy = strategy;
    }
    setStrategy(strategy) {
        this.strategy = strategy;
    }
    makePayment(amount) {
        return this.strategy.processPayment(amount);
    }
}
// Client Code
const creditCardPayment = new PaymentProcessor(new CreditCardStrategy());
console.log(creditCardPayment.makePayment(100));
const paypalPayment = new PaymentProcessor(new PayPalStrategy());
console.log(paypalPayment.makePayment(200));

Output:

Processed payment of $100 using Credit Card.
Processed payment of $200 using PayPal.

Explanation:

1. A PaymentStrategy interface is defined which encapsulates the payment processing method.

2. Concrete strategies (CreditCardStrategy and PayPalStrategy) are implemented. Each strategy has its own version of the payment processing algorithm.

3. The PaymentProcessor (context) class holds a reference to a strategy. It uses the strategy to process payments.

4. In the client code, the PaymentProcessor class is instantiated with specific strategies to demonstrate different payment processing methods.

7. When to use?

The Strategy Pattern is beneficial when:

1. There are multiple ways (algorithms) to perform a task, and they're interchangeable.

2. Algorithms need to be selected and swapped at runtime.

3. The application needs to be open for extension but closed for modification (Open-Closed Principle).


Comments