Lesson 3
Mastering the Strategy Pattern
Mastering the Strategy Pattern

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 C#. We will break down the pattern into manageable parts and illustrate its practical application through a clear example.

Understanding the Strategy Pattern

The Strategy Pattern is like choosing the best tool from a toolbox for a specific job. It allows a class to switch between different algorithms (strategies) seamlessly without changing its code.

Consider a scenario where you have a ShoppingCart class that needs to handle payments using different methods, like credit cards or PayPal. With the Strategy Pattern, we can encapsulate these payment methods into separate classes and let the ShoppingCart class use any of these strategies interchangeably.

The main components of the Strategy Pattern are:

  1. Strategy Interface: Defines a common interface for all strategies.
  2. Concrete Strategies: Specific classes implementing the strategies.
  3. Context Class: A class that uses the strategies.

Let's dive right in and start implementing this pattern to our payments scenario.

Strategy Interface

First, we need to define an interface that all payment strategies will implement. This ensures that all payment methods follow a common interface and can be used interchangeably.

C#
1// Define the interface for the payment strategy 2interface IPaymentStrategy 3{ 4 void Pay(int amount); 5}

In this snippet, we create an interface IPaymentStrategy with a method Pay. Any class that implements IPaymentStrategy must provide an implementation of the Pay method.

Concrete Strategies: Credit Card Payment

Next, we implement concrete strategies that encapsulate different payment methods. Here, we start with the CreditCardStrategy.

C#
1// Implementation of the CreditCard strategy 2class CreditCardStrategy : IPaymentStrategy 3{ 4 private string cardNumber; 5 6 // Constructor to initialize the card number 7 public CreditCardStrategy(string cardNumber) 8 { 9 this.cardNumber = cardNumber; 10 } 11 12 // Implement the Pay method for CreditCard strategy 13 public void Pay(int amount) 14 { 15 Console.WriteLine($"Paid {amount} using Credit Card: {cardNumber}"); 16 } 17}

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

Concrete Strategies: PayPal Payment

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

C#
1// Implementation of the PayPal strategy 2class PayPalStrategy : IPaymentStrategy 3{ 4 private string email; 5 6 // Constructor to initialize the email 7 public PayPalStrategy(string email) 8 { 9 this.email = email; 10 } 11 12 // Implement the Pay method for PayPal strategy 13 public void Pay(int amount) 14 { 15 Console.WriteLine($"Paid {amount} using PayPal: {email}"); 16 } 17}
Context Class

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

C#
1// ShoppingCart class that uses a payment strategy 2class ShoppingCart 3{ 4 private IPaymentStrategy? strategy; 5 6 // Set the payment strategy 7 public void SetPaymentStrategy(IPaymentStrategy strategy) 8 { 9 this.strategy = strategy; 10 } 11 12 // Perform checkout with the set payment strategy 13 public void Checkout(int amount) 14 { 15 if (strategy != null) 16 { 17 strategy.Pay(amount); 18 } 19 else 20 { 21 Console.WriteLine("No payment strategy set."); 22 } 23 } 24}

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.

Integration in the Main Program

Finally, let's see how the ShoppingCart class and the different payment strategies work together in the main program.

C#
1class Program 2{ 3 static void Main() 4 { 5 ShoppingCart cart = new ShoppingCart(); 6 7 // Create payment strategies 8 CreditCardStrategy creditCard = new CreditCardStrategy("1234-5678-9876-5432"); 9 PayPalStrategy payPal = new PayPalStrategy("user@example.com"); 10 11 // Use CreditCard strategy and checkout 12 cart.SetPaymentStrategy(creditCard); 13 cart.Checkout(100); 14 // Output: Paid 100 using Credit Card: 1234-5678-9876-5432 15 16 // Switch to PayPal strategy and checkout 17 cart.SetPaymentStrategy(payPal); 18 cart.Checkout(200); 19 // Output: Paid 200 using PayPal: user@example.com 20 } 21}

In this example, we:

  1. Create a ShoppingCart.
  2. Make payment strategies for credit card and PayPal.
  3. Set the cart to use the credit card strategy and check out.
  4. Switch the cart to use the PayPal strategy and check out again.

By setting different payment strategies, the ShoppingCart can seamlessly switch between them without needing to change its internal code.

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.