Lesson 3
Implementing the Strategy Pattern in JavaScript
Introduction to the Strategy Pattern

Welcome back! We are continuing our journey through Behavioral Patterns in software design. In previous lessons, we explored the Command and Observer patterns, focusing on object communication and state changes. Now, we will learn how to implement the Strategy Pattern in JavaScript. We will break down the pattern into manageable parts and illustrate its practical application through a clear example.

Consider a scenario where you have a ShoppingCart class that can handle payments through different methods, such as credit cards or PayPal. Using the Strategy Pattern, we can encapsulate these payment methods into separate classes and have the ShoppingCart class use any of these strategies interchangeably.

Strategy Interface

First, we need to simulate an interface that all payment strategies will follow. In JavaScript, we do this by defining a common method that all payment strategy classes should implement.

JavaScript
1class PaymentStrategy { 2 pay(amount) { 3 throw new Error("Method 'pay()' must be implemented."); 4 } 5}

In this snippet, we create a PaymentStrategy class that enforces the implementation of the pay method in any class extending it by checking in the constructor.

Concrete Strategies

Next, we implement concrete strategies that encapsulate different payment methods. Here, we define two strategies: CreditCardStrategy and PayPalStrategy.

JavaScript
1class CreditCardStrategy extends PaymentStrategy { 2 constructor(cardNumber) { 3 super(); 4 this.cardNumber = cardNumber; 5 } 6 7 pay(amount) { 8 console.log(`Paid ${amount} using Credit Card: ${this.cardNumber}`); 9 } 10}

In the CreditCardStrategy class, we implement the pay method to handle credit card transactions. This class requires a card number upon initialization.

JavaScript
1class PayPalStrategy extends PaymentStrategy { 2 constructor(email) { 3 super(); 4 this.email = email; 5 } 6 7 pay(amount) { 8 console.log(`Paid ${amount} using PayPal: ${this.email}`); 9 } 10}

Similarly, the PayPalStrategy class implements the pay method for PayPal transactions. It requires an email address to initialize.

Context Class

The ShoppingCart class is our context class that will use any given payment strategy. This class keeps a reference to a PaymentStrategy object and can switch strategies at runtime.

JavaScript
1class ShoppingCart { 2 constructor() { 3 this.strategy = null; 4 } 5 6 setPaymentStrategy(strategy) { 7 this.strategy = strategy; 8 } 9 10 checkout(amount) { 11 if (this.strategy) { 12 this.strategy.pay(amount); 13 } else { 14 console.log("No payment strategy set."); 15 } 16 } 17}

In the ShoppingCart class, the setPaymentStrategy method allows us to set the payment strategy, and the checkout method uses the selected strategy to make a payment.

Full Example Code

Below is the complete code for our example, integrating all the parts we've discussed:

JavaScript
1class PaymentStrategy { 2 pay(amount) { 3 throw new Error("Method 'pay()' must be implemented."); 4 } 5} 6 7class CreditCardStrategy extends PaymentStrategy { 8 constructor(cardNumber) { 9 super(); 10 this.cardNumber = cardNumber; 11 } 12 13 pay(amount) { 14 console.log(`Paid ${amount} using Credit Card: ${this.cardNumber}`); 15 } 16} 17 18class PayPalStrategy extends PaymentStrategy { 19 constructor(email) { 20 super(); 21 this.email = email; 22 } 23 24 pay(amount) { 25 console.log(`Paid ${amount} using PayPal: ${this.email}`); 26 } 27} 28 29class ShoppingCart { 30 constructor() { 31 this.strategy = null; 32 } 33 34 setPaymentStrategy(strategy) { 35 this.strategy = strategy; 36 } 37 38 checkout(amount) { 39 if (this.strategy) { 40 this.strategy.pay(amount); 41 } else { 42 console.log("No payment strategy set."); 43 } 44 } 45} 46 47const cart = new ShoppingCart(); 48 49const creditCard = new CreditCardStrategy("1234-5678-9876-5432"); 50const paypal = new PayPalStrategy("user@example.com"); 51 52cart.setPaymentStrategy(creditCard); 53cart.checkout(100); // Outputs: Paid 100 using Credit Card: 1234-5678-9876-5432 54 55cart.setPaymentStrategy(paypal); 56cart.checkout(200); // Outputs: Paid 200 using PayPal: user@example.com
Conclusion

Understanding the Strategy Pattern is crucial because it promotes flexibility and reusability in your code. Instead of hardcoding multiple algorithms within a class, you can encapsulate them into separate strategy classes. This makes your code more maintainable and scalable. Consider a real-world example: an e-commerce platform. One customer might prefer to pay using a credit card, while another might choose PayPal. With the Strategy Pattern, you can easily switch payment methods without altering the underlying business logic of the shopping cart. By mastering the Strategy Pattern, you'll be equipped to build systems that can adapt to varying requirements with minimal changes to the codebase.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.