C++ Strategy Pattern Example

1. Definition

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

2. Problem Statement

Consider a scenario where you have a class that performs an operation, but there are multiple ways (strategies) to implement that operation. Using conditional statements (like if-else or switch-case) to choose between these strategies can make the code complex, rigid, and hard to maintain.

3. Solution

Encapsulate each strategy (algorithm) in a separate class derived from a common interface. The client class can then use any strategy object interchangeably, enabling the flexibility to switch strategies dynamically.

4. Real-World Use Cases

1. Different sorting algorithms (QuickSort, MergeSort, BubbleSort) that can be used interchangeably.

2. Various compression algorithms (ZIP, RAR, TAR) for a file compressor application.

3. Different payment methods (Credit Card, PayPal, Bitcoin) in an e-commerce application.

5. Implementation Steps

1. Define a Strategy interface which declares an action.

2. Implement concrete strategy classes for each variant of the algorithm.

3. Create a context class that uses a strategy object to perform the action.

6. Implementation in C++

// Strategy Interface
class SortingStrategy {
public:
    virtual void sort(std::vector<int>& data) = 0;
};
// Concrete Strategies
class BubbleSort : public SortingStrategy {
public:
    void sort(std::vector<int>& data) override {
        // Simple Bubble Sort implementation
        int n = data.size();
        for (int i = 0; i < n-1; i++) {
            for (int j = 0; j < n-i-1; j++) {
                if (data[j] > data[j+1]) {
                    std::swap(data[j], data[j+1]);
                }
            }
        }
    }
};
class QuickSort : public SortingStrategy {
public:
    void sort(std::vector<int>& data) override {
        // QuickSort implementation (omitted for brevity)
    }
};
// Context Class
class SortedList {
    SortingStrategy* strategy;
    std::vector<int> data;
public:
    SortedList(SortingStrategy* s) : strategy(s) {}
    void setStrategy(SortingStrategy* s) {
        strategy = s;
    }
    void add(int value) {
        data.push_back(value);
    }
    void sortAndPrint() {
        strategy->sort(data);
        for (int value : data) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
};
// Example Usage
int main() {
    SortedList list(new BubbleSort());
    list.add(5);
    list.add(2);
    list.add(8);
    list.add(1);
    list.sortAndPrint(); // Using BubbleSort
    list.setStrategy(new QuickSort());
    list.sortAndPrint(); // Using QuickSort
    return 0;
}

Output:

1 2 5 8
1 2 5 8

Explanation:

1. We first define a SortingStrategy interface that has a sort method.

2. BubbleSort and QuickSort are concrete implementations of this strategy. For the sake of brevity, we've only provided an implementation for BubbleSort.

3. The SortedList context class maintains data and a reference to a strategy. It can use any strategy object to perform sorting.

4. In the example, we initially use BubbleSort to sort and print the list. We then switch to QuickSort and sort the list again.

7. When to use?

Use the Strategy pattern when:

1. You want to choose an algorithm's implementation at runtime.

2. Various related classes only differ in their behavior.

3. You need to hide algorithm implementations from the client.


Comments