Lesson 3
Introduction to the Strategy Pattern
Introduction to the Strategy Pattern

Welcome back! We are continuing our journey through Behavioral Patterns in C++. In previous lessons, we explored the Command and Observer patterns, focusing on object communication and state changes. Now, we will delve into the Strategy Pattern.

What You'll Learn

In this lesson, you 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.

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.

Here is a snippet of the code we will be working with:

We will start by defining the PaymentStrategy interface, which declares a common method for all payment strategies. Then, we will create concrete strategies such as CreditCardStrategy and PayPalStrategy that implement this interface:

C++
1#include <iostream> 2#include <string> 3 4class PaymentStrategy { 5public: 6 virtual ~PaymentStrategy() = default; 7 virtual void pay(int amount) = 0; 8}; 9 10class CreditCardStrategy : public PaymentStrategy { 11public: 12 CreditCardStrategy(const std::string& cardNumber) : cardNumber(cardNumber) {} 13 14 void pay(int amount) override { 15 std::cout << "Paid " << amount << " using Credit Card: " << cardNumber << std::endl; 16 } 17 18private: 19 std::string cardNumber; 20}; 21 22class PayPalStrategy : public PaymentStrategy { 23public: 24 PayPalStrategy(const std::string& email) : email(email) {} 25 26 void pay(int amount) override { 27 std::cout << "Paid " << amount << " using PayPal: " << email << std::endl; 28 } 29 30private: 31 std::string email; 32};

Next, we will create a ShoppingCart class that can set a payment strategy and use it to perform the payment operation:

C++
1class ShoppingCart { 2public: 3 void setPaymentStrategy(PaymentStrategy* strategy) { 4 this->strategy = strategy; 5 } 6 7 void checkout(int amount) { 8 if (strategy) { 9 strategy->pay(amount); 10 } else { 11 std::cout << "No payment strategy set." << std::endl; 12 } 13 } 14 15private: 16 PaymentStrategy* strategy = nullptr; 17};

Finally, we will demonstrate how to use the ShoppingCart class with different payment strategies:

C++
1int main() { 2 ShoppingCart cart; 3 4 CreditCardStrategy creditCard("1234-5678-9876-5432"); 5 PayPalStrategy payPal("user@example.com"); 6 7 cart.setPaymentStrategy(&creditCard); 8 cart.checkout(100); 9 10 cart.setPaymentStrategy(&payPal); 11 cart.checkout(200); 12 13 return 0; 14}

Let's analyze the key components of the Strategy Pattern through this example:

  • Strategy Interface (PaymentStrategy): An interface that defines a common method (in this case, pay) for all concrete strategies.
  • Concrete Strategies (CreditCardStrategy, PayPalStrategy): Classes that implement the PaymentStrategy interface, providing specific implementations of the pay method.
  • Context (ShoppingCart): The class that uses a PaymentStrategy object. This class can set the strategy at runtime and use it to perform its payment operations.
Use Cases of the Strategy Pattern

The Strategy Pattern is ideal for scenarios where multiple algorithms or behaviors can be used interchangeably. Some common use cases include:

  1. Payment Systems: As demonstrated in the example, an application can support multiple payment methods such as credit cards, PayPal, or cryptocurrencies.
  2. Sorting Algorithms: In applications where data needs to be sorted, you can use different sorting algorithms (e.g., quicksort, mergesort) without altering the core functionality of the class using them.
  3. Compression Libraries: Applications that handle data compression can use various compression algorithms (e.g., ZIP, RAR) interchangeably by implementing different compression strategies.
  4. Document Converters: A document conversion tool might support multiple formats, such as converting between PDF and Word, using different strategies for each format.
  5. Game Development: Different behaviors for game characters (e.g., different attack strategies or movement patterns) can be encapsulated into separate strategy classes, making it easy to switch behaviors dynamically.
Pros and Cons

Pros

  1. Flexibility: The Strategy Pattern allows you to switch algorithms or behaviors at runtime, providing high flexibility in how tasks are performed.
  2. Reusability: Encapsulation of algorithms into separate classes promotes reusability of these classes across different parts of the application or even in different projects.
  3. Simplified Code Maintenance: With the Strategy Pattern, changes to an algorithm can be made independently without altering the context class, making the codebase easier to maintain.
  4. Single Responsibility Principle: By separating different behaviors or algorithms, each class has a specific responsibility, aligning with the Single Responsibility Principle and leading to cleaner, more understandable code.

Cons

  1. Increased Number of Classes: The Strategy Pattern can lead to a proliferation of classes representing each strategy, which might make the codebase more complex to navigate.
  2. Overhead of Strategy Management: In scenarios where the strategy needs to be changed frequently, there might be some performance overhead associated with strategy management.
  3. Client Awareness: The client code must be aware of the different strategies and how to switch between them, which can add complexity to the client's implementation.
  4. Additional Layer of Indirection: Introducing strategy interfaces and concrete strategies adds an additional layer of indirection, which may lead to more complex debugging and understanding of the flow of control.

By weighing these pros and cons, you can determine whether the Strategy Pattern is the best fit for your specific application requirements.

Why It Matters

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.

Exciting, right? By mastering the Strategy Pattern, you'll be equipped to build systems that can adapt to varying requirements with minimal changes to the codebase. Let's move on to the practice section and see these concepts in action!

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