PHP Strategy Design Pattern Поведенческий паттерн

Паттерн Стратегия (Strategy) в PHP

Короткий скринкаст: паттерн «Стратегия» (Strategy) — алгоритмы можно подменять во время выполнения, не трогая клиента. Пример с разными способами оплаты.

Strategy Pattern — открыть страницу урока

Описание

Стратегия позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми. Клиент выбирает нужный алгоритм во время выполнения, передавая его в контекст. Интерфейс PaymentStrategy описывает общий метод оплаты. Конкретные стратегии CreditCardPayment и PaypalPayment реализуют разные способы оплаты. Контекст ShoppingCart принимает стратегию через конструктор и делегирует оплату выбранной стратегии в checkout() — сам он не знает, каким именно способом происходит оплата.

UML-диаграмма

classDiagram class PaymentStrategy { <<interface>> +pay(amount: int): void } class CreditCardPayment { -cardNumber: string +pay(amount: int): void } class PaypalPayment { -email: string +pay(amount: int): void } class ShoppingCart { -strategy: PaymentStrategy +checkout(amount: int): void } PaymentStrategy <|.. CreditCardPayment PaymentStrategy <|.. PaypalPayment ShoppingCart o-- PaymentStrategy

Код

PaymentStrategy.php

interface PaymentStrategy
{
    public function pay(int $amount): void;
}

CreditCardPayment.php

class CreditCardPayment implements PaymentStrategy
{
    public function __construct(private readonly string $cardNumber) {}

    public function pay(int $amount): void
    {
        echo "Paid {$amount} via Credit Card ({$this->cardNumber})\n";
    }
}

PaypalPayment.php

class PaypalPayment implements PaymentStrategy
{
    public function __construct(private readonly string $email) {}

    public function pay(int $amount): void
    {
        echo "Paid {$amount} via PayPal ({$this->email})\n";
    }
}

ShoppingCart.php

class ShoppingCart
{
    public function __construct(private readonly PaymentStrategy $strategy) {}

    public function checkout(int $amount): void
    {
        $this->strategy->pay($amount);
    }
}

demo.php

require_once 'PaymentStrategy.php';
require_once 'CreditCardPayment.php';
require_once 'PaypalPayment.php';
require_once 'ShoppingCart.php';

$cart = new ShoppingCart(new CreditCardPayment('1234-5678-9012-3456'));
$cart->checkout(100);

$cart = new ShoppingCart(new PaypalPayment('user@example.com'));
$cart->checkout(200);

Пример результата

Paid 100 via Credit Card (1234-5678-9012-3456)
Paid 200 via PayPal (user@example.com)

Преимущества

  • Алгоритмы инкапсулированы и легко взаимозаменяемы.
  • Клиент отделён от деталей реализации алгоритмов.
  • Можно добавлять новые стратегии, не меняя существующий код.
  • Следует принципу открытости/закрытости и инверсии зависимостей.

Когда использовать

  • Когда нужно выбирать один из нескольких алгоритмов динамически.
  • Когда множество классов различаются только поведением.
  • Когда нужно скрыть детали алгоритма от клиента.
  • Когда поведение может меняться без изменения контекста.

Использование в Laravel

В Laravel Стратегия встречается при выборе различных драйверов: например, разные драйверы очередей (redis, database, sqs), разные способы авторизации, или различные стратегии оплаты в пакетах. Также Illuminate\Support\Facades\Cache позволяет выбирать драйвер кеша во время работы приложения. Контекст использует выбранную стратегию, не зная деталей её реализации.