Strategy Design Pattern

In software design, one of the most common challenges is handling different algorithms or behaviors that can be swapped without changing the rest of the code. This is where the Strategy Pattern shines.

The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm’s behavior at runtime. Instead of hardcoding different cases into a class, you encapsulate each behavior in its own class and make them interchangeable.

A good time to use the Strategy Pattern would be when you have multiple ways of performing a specific task (e.g., sorting, payment methods, logging), when you want to avoid long if...else or switch statements or when you need to change the algorithm at runtime without modifying the calling code.

The Strategy Pattern consists of three things:

a. an interface

b. the concrete implementation of the different algorithms

c. a context class


PHP Example

Step 1: Strategy Interface

interface PaymentStrategy {
    public function pay(float $amount): void;
}
                                        
Step 2: Concrete Strategies

class CreditCardPayment implements PaymentStrategy {
    public function pay(float $amount): void {
        echo "Paid $amount using Credit Card.\n";
    }
}

class PayPalPayment implements PaymentStrategy {
    public function pay(float $amount): void {
        echo "Paid $amount using PayPal.\n";
    }
}
                                        
Step 3: Context Class

class PaymentContext {
    private PaymentStrategy $paymentStrategy;

    public function __construct(PaymentStrategy $paymentStrategy) {
        $this->paymentStrategy = $paymentStrategy;
    }

    public function setPaymentStrategy(PaymentStrategy $paymentStrategy): void {
        $this->paymentStrategy = $paymentStrategy;
    }

    public function checkout(float $amount): void {
        $this->paymentStrategy->pay($amount);
    }
}
                                        
Final step: Using it

$context = new PaymentContext(new CreditCardPayment());
$context->checkout(100.0); // Paid 100 using Credit Card.

$context->setPaymentStrategy(new PayPalPayment());
$context->checkout(200.0); // Paid 200 using PayPal.

                                        

The Strategy Pattern can be combined with a Factory Pattern for the selection of the right Algorithm class that the Context Class will receive.

The Factory

class PaymentFactory
{
    public static function make($option)
    {
        if($option == 1){
            return new CreditCardPayment();
        }

        if($option == 2){
            return new PayPalPayment();
        }

        throw Exception('Class not found');
    }
}
                                        
Using the Factory

$option = $request->input('option');
$paymentObject = PaymentFactory::make($option);
$context = new PaymentContext($paymentObject);
$context->checkout(100.0);
                                        

This can be a drawback of the Strategy Pattern. In some frameworks the Strategy Pattern can be used with the .env configuration file i.e. the logging algorithm (daily/stack) or the session in Laravel. With the Strategy Pattern you comply to the open/close principle and you have code reusability but the same time you increase the number of classes.