Fundamentals 11 min read

Understanding the Strategy Design Pattern: Principles, Benefits, and PHP Implementations

This article explains the Strategy design pattern, its alignment with SOLID principles such as Single Responsibility and Open/Closed, and demonstrates how it improves code maintainability, reusability, testability, and flexibility through clear PHP examples for payments, sorting, discounts, and shipping.

php中文网 Courses
php中文网 Courses
php中文网 Courses
Understanding the Strategy Design Pattern: Principles, Benefits, and PHP Implementations

In software development, writing clear, concise, and maintainable code is essential, and design patterns act as a lighthouse guiding developers; among them, the Strategy pattern stands out for its ability to separate algorithm definitions from their usage, enabling flexible replacement without modifying client code.

The pattern aligns with the Single Responsibility Principle (SRP) by encapsulating each algorithm in its own class, preventing controllers or services from becoming overloaded with multiple responsibilities and promoting modular, reusable code.

It also embodies the Open/Closed Principle, allowing new strategies (e.g., BankTransferPayment) to be added without altering existing code, thus enhancing extensibility and reducing the risk of bugs.

By encapsulating varying behaviors, the Strategy pattern boosts code reusability; the same sorting strategies (PriceSort, NameSort) or payment strategies can be applied across different contexts without duplication.

Because each strategy is an independent class, testing becomes straightforward—strategies can be mocked or swapped, as shown in the PHPUnit example for testing product sorting.

The pattern simplifies complex business logic by breaking it into focused strategies, such as different discount calculations (PercentageDiscount, FixedDiscount) or shipping cost calculations (FedExShipping, UPSShipping), making the code easier to understand and extend.

Overall, the Strategy pattern provides runtime flexibility, allowing applications to switch behaviors dynamically, which is especially valuable in e‑commerce scenarios where payment, sorting, discount, and shipping options vary.

In conclusion, the Strategy pattern adheres to SOLID principles, improves reusability, testability, and flexibility, and helps developers build robust, maintainable, and adaptable software architectures.

// Strategy interface
interface PaymentStrategy {
    public function pay(string $amount): void;
}

// Concrete strategies
class PaypalPayment implements PaymentStrategy {
    public function pay(string $amount): void {
        // PayPal payment logic
        echo "Paid $amount using PayPal.";
    }
}

class StripePayment implements PaymentStrategy {
    public function pay(string $amount): void {
        // Stripe payment logic
        echo "Paid $amount using Stripe.";
    }
}

// Context
class PaymentProcessor {
    public function __construct(protected PaymentStrategy $paymentStrategy) {}
    public function process(string $amount): void {
        $this->paymentStrategy->pay($amount);
    }
}

// Usage
$paymentProcessor = new PaymentProcessor(new PaypalPayment());
$paymentProcessor->process('100'); // Outputs: Paid 100 using PayPal.
class BankTransferPayment implements PaymentStrategy {
    public function pay(string $amount): void {
        // Bank transfer logic
        echo "Paid $amount using Bank Transfer.";
    }
}

// Adding new strategy without changing existing code
$paymentProcessor = new PaymentProcessor(new BankTransferPayment());
$paymentProcessor->process('100'); // Outputs: Paid 100 using Bank Transfer.
// Strategy interface for sorting
interface SortStrategy {
    public function sort(array $products): array;
}

class PriceSort implements SortStrategy {
    public function sort(array $products): array {
        usort($products, fn($a, $b) => $a['price'] <=> $b['price']);
        return $products;
    }
}

class NameSort implements SortStrategy {
    public function sort(array $products): array {
        usort($products, fn($a, $b) => $a['name'] <=> $b['name']);
        return $products;
    }
}

class ProductSorter {
    public function __construct(protected SortStrategy $sortStrategy) {}
    public function sortProducts(array $products): array {
        return $this->sortStrategy->sort($products);
    }
}

$products = [
    ['name' => 'Product A', 'price' => 100],
    ['name' => 'Product B', 'price' => 50],
];
$productSorter = new ProductSorter(new PriceSort());
$sortedProducts = $productSorter->sortProducts($products); // Sorted by price
// Strategy interface for discounts
interface DiscountStrategy {
    public function calculate(float $price): float;
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(protected float $percentage) {}
    public function calculate(float $price): float {
        return $price - ($price * $this->percentage / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(protected float $discount) {}
    public function calculate(float $price): float {
        return $price - $this->discount;
    }
}

class PriceCalculator {
    public function __construct(protected DiscountStrategy $discountStrategy) {}
    public function calculatePrice(float $price): float {
        return $this->discountStrategy->calculate($price);
    }
}

$calculator = new PriceCalculator(new PercentageDiscount(10));
$finalPrice = $calculator->calculatePrice(200); // Applies 10% discount
// Strategy interface for shipping
interface ShippingStrategy {
    public function calculateShippingCost(float $weight): float;
}

class FedExShipping implements ShippingStrategy {
    public function calculateShippingCost(float $weight): float {
        return $weight * 1.2; // FedEx rate
    }
}

class UPSShipping implements ShippingStrategy {
    public function calculateShippingCost(float $weight): float {
        return $weight * 1.5; // UPS rate
    }
}

class Order {
    public function __construct(protected ShippingStrategy $shippingStrategy) {}
    public function calculateTotalCost(float $weight): float {
        return $this->shippingStrategy->calculateShippingCost($weight);
    }
}

$order = new Order(new FedExShipping());
$shippingCost = $order->calculateTotalCost(10); // Uses FedEx calculation
design patternssoftware architectureStrategy PatternTestingPHPSOLIDCode Reusability
php中文网 Courses
Written by

php中文网 Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.