Mastering the Strategy Design Pattern in PHP

| Tags: #php #design-patterns

The strategy design pattern is a behavioral design pattern that allows an object to change its behavior based on a given context. The pattern involves separating an object’s behavior into different strategies and allowing the object to switch between these strategies at runtime. This allows for greater flexibility and maintainability in the code, as the behavior of the object can be changed without modifying the object itself.

In PHP, the strategy design pattern can be implemented by creating an interface or abstract class that defines the behavior that all strategies must have. Concrete classes are then created that implement this interface or extend the abstract class, providing the specific implementation for each strategy.

To illustrate this pattern, let’s consider an example of a shopping cart that can calculate the total cost of the items in it. Different strategies can be used to calculate the total cost, such as using a discount code, calculating shipping costs, or applying taxes.

First, we can create an interface for the strategy, which defines the methods that all strategies must implement. In this example, the strategy interface is called CalculateTotalCostStrategy and it has only one method calculate():

interface CalculateTotalCostStrategy {
    public function calculate(array $items);
}

We can then create concrete classes that implement this strategy. For example, one concrete strategy can be called DiscountCodeStrategy, which applies a discount code to the total cost:

class DiscountCodeStrategy implements CalculateTotalCostStrategy {
    public function calculate(array $items) {
        $discount = 10;
        // logic to apply discount code
        return array_sum($items) - $discount;
    }
}

Another concrete strategy can be called ShippingCostStrategy, which calculates shipping costs for the items:

class ShippingCostStrategy implements CalculateTotalCostStrategy {
    public function calculate(array $items) {
        $shippingCost = 10;
        // logic to calculate shipping costs
        return array_sum($items) + $shippingCost;
    }
}

We can also create a context class, which is the class that will change its behavior based on the strategy selected. The context class will have a property strategy that will hold the current strategy.

class ShoppingCart {
    private $strategy;
    
    public function __construct(CalculateTotalCostStrategy $strategy) {
        $this->strategy = $strategy;
    }
    
    public function calculateTotalCost(array $items) {
        return $this->strategy->calculate($items);
    }
}

Once the context class, interface and the concrete strategies are defined, we can use them to create the objects and switch between strategies at runtime.

$items = array(10, 20, 30);

$shoppingCart = new ShoppingCart(new DiscountCodeStrategy());
echo $shoppingCart->calculateTotalCost($items);
// output: 50 (total cost of items - discount)

$shoppingCart = new ShoppingCart(new ShippingCostStrategy());
echo $shoppingCart->calculateTotalCost($items);
// output: 70 (total cost of items + shipping cost)

This is a simple example of how the strategy pattern can be implemented