Design patterns: Strategy

Mauto 3 min

The Strategy Pattern is a behavioral pattern that lets you define a family of algorithms, encapsulate each one in a separate class, and make them interchangeable. The algorithm to use can be selected dynamically at runtime, making the code more flexible and easy to extend. This pattern is particularly useful when you want to avoid long conditional blocks (such as if-else or switch-case) to choose between different algorithms.

Strengths

  1. Code separation: Each algorithm is encapsulated in its own class, separating the algorithm’s logic from the context in which it’s used.
  2. Interchangeable algorithms: Algorithms can be swapped at runtime without changing the client code.
  3. Better maintainability: Adding or changing algorithms doesn’t affect the rest of the code.
  4. Extensibility: It’s easy to add new algorithms by simply implementing a new class that follows the same interface.
  5. Promotes cohesion: The context handles only the high-level logic, while the strategies take care of the specific implementation of the algorithms.

Use case: A payment system

Imagine a payment system that supports several methods: Credit Card, PayPal and Bank Transfer. Depending on the payment method chosen by the user, the algorithm for processing the payment changes.

PHP Code

<?php
// Payment Strategy interface
interface PaymentStrategy {
    public function pay($amount);
}

// Credit Card payment strategy
class CreditCardPayment implements PaymentStrategy {
    private $cardNumber;

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

    public function pay($amount) {
        echo "Payment of $amount made with Credit Card number: {$this->cardNumber}.\n";
    }
}

// PayPal payment strategy
class PayPalPayment implements PaymentStrategy {
    private $email;

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

    public function pay($amount) {
        echo "Payment of $amount made with PayPal to the address: {$this->email}.\n";
    }
}

// Bank Transfer payment strategy
class BankTransferPayment implements PaymentStrategy {
    private $bankAccount;

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

    public function pay($amount) {
        echo "Payment of $amount made by Bank Transfer to the account: {$this->bankAccount}.\n";
    }
}

// Context class for payment
class PaymentContext {
    private $paymentStrategy;

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

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

    public function pay($amount) {
        $this->paymentStrategy->pay($amount);
    }
}

// Usage example
$context = new PaymentContext(new CreditCardPayment("1234-5678-9876-5432"));
$context->pay(100);  // Payment by Credit Card

$context->setPaymentStrategy(new PayPalPayment("user@example.com"));
$context->pay(200);  // Payment by PayPal

$context->setPaymentStrategy(new BankTransferPayment("IT60X0542811101000000123456"));
$context->pay(300);  // Payment by Bank Transfer

Explanation

  • PaymentStrategy interface: Defines the pay() method, which every payment strategy must implement.
  • Concrete Strategies (CreditCardPayment, PayPalPayment, BankTransferPayment): Implement the interface and handle the payment algorithm for their respective method.
  • PaymentContext class: Manages the current payment algorithm and lets you switch strategy at runtime.
  • Using the context: The user can change the payment method without modifying the context itself; the strategy is simply replaced.

Conclusion

The Strategy Pattern cleanly separates algorithms from the context in which they’re used, improving modularity and making algorithms interchangeable at runtime. This is particularly useful in complex systems that require a variety of algorithms or logic.

BACK TO THE GUIDE

Copiato