Strategy Pattern in PHP with examples

We need to implement a sorting system that sorts an array of numbers in ascending or descending order

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Sorter {
    public function bubbleSort(array $arr) {
        // Implementation of Bubble Sort
    }
    
    public function quickSort(array $arr) {
        // Implementation of Quick Sort
    }
    
    public function mergeSort(array $arr) {
        // Implementation of Merge Sort
    }
}

$sorter = new Sorter();
$sortedArr = $sorter->bubbleSort($arr); // or $sorter->quickSort($arr) or $sorter->mergeSort($arr)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
interface SortingStrategy {
    public function sort(array $arr): array;
}
class BubbleSort implements SortingStrategy {
    public function sort(array $arr): array {
        // Implementation of Bubble Sort
    }
}
class QuickSort implements SortingStrategy {
    public function sort(array $arr): array {
        // Implementation of Quick Sort
    }
}
class MergeSort implements SortingStrategy {
    public function sort(array $arr): array {
        // Implementation of Merge Sort
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Sorter {
    private $sortingStrategy;
    
    public function __construct(SortingStrategy $sortingStrategy) {
        $this->sortingStrategy = $sortingStrategy;
    }
    
    public function sort(array $arr): array {
        return $this->sortingStrategy->sort($arr);
    }
}
$sorter = new Sorter(new BubbleSort());
$sortedArr = $sorter->sort($arr); // or new Sorter(new QuickSort())->sort($arr) or new Sorter(new MergeSort())->sort($arr)

We want to implement the discount system

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class DiscountCalculator {
    public function calculateDiscount(Product $product) {
        if ($product instanceof Book) {
            return $product->getPrice() * 0.1;
        } elseif ($product instanceof Clothing) {
            return $product->getPrice() * 0.2;
        } elseif ($product instanceof Electronics) {
            return $product->getPrice() * 0.3;
        }
    }
}
$discountCalculator = new DiscountCalculator();
$discountedPrice = $product->getPrice() - $discountCalculator->calculateDiscount($product);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
interface DiscountStrategy {
    public function calculateDiscount(Product $product): float;
}
class BookDiscount implements DiscountStrategy {
    public function calculateDiscount(Product $product): float {
        return $product->getPrice() * 0.1;
    }
}
class ClothingDiscount implements DiscountStrategy {
    public function calculateDiscount(Product $product): float {
        return $product->getPrice() * 0.2;
    }
}
class ElectronicsDiscount implements DiscountStrategy {
    public function calculateDiscount(Product $product): float {
        return $product->getPrice() * 0.3;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class DiscountCalculator {
    private $discountStrategy;
    
    public function __construct(DiscountStrategy $discountStrategy) {
        $this->discountStrategy = $discountStrategy;
    }
    
    public function calculateDiscount(Product $product): float {
        return $this->discountStrategy->calculateDiscount($product);
    }
}
$discountCalculator = new DiscountCalculator(new BookDiscount());
$discountedPrice = $product->getPrice() - $discountCalculator->calculateDiscount($product);
  • Keep the Strategy interface simple and focused on the algorithm’s behavior.
  • Use meaningful names for the Concrete Strategies to make the code more readable and maintainable.
  • Use dependency injection to provide the Concrete Strategies to the Context class.
  • Avoid coupling the Concrete Strategies to the Context class to make the code more flexible and reusable.

Related Content