Что такое паттерн Стратегия (Strategy)?

«Что такое паттерн Стратегия (Strategy)?» — вопрос из категории Паттерны, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Strategy (Стратегия) — это поведенческий шаблон, который определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Он позволяет изменять алгоритм независимо от клиента, который его использует.

Основная цель: Замена условных операторов (if/switch) на полиморфизм объектов.

Пример на PHP (система оплаты):

// Интерфейс стратегии
interface PaymentStrategy {
    public function pay(float $amount): string;
}

// Конкретные стратегии
class CreditCardPayment implements PaymentStrategy {
    private string $cardNumber;
    private string $cvv;

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

    public function pay(float $amount): string {
        // Логика взаимодействия с платежным шлюзом для карт
        return "Paid $amount using Credit Card ending with " . substr($this->cardNumber, -4);
    }
}

class PayPalPayment implements PaymentStrategy {
    private string $email;

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

    public function pay(float $amount): string {
        // Логика API PayPal
        return "Paid $amount using PayPal account: $this->email";
    }
}

class CryptoPayment implements PaymentStrategy {
    private string $walletAddress;

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

    public function pay(float $amount): string {
        // Логика работы с блокчейном
        return "Paid $amount in crypto to wallet: " . substr($this->walletAddress, 0, 8) . "...";
    }
}

// Контекст, использующий стратегию
class ShoppingCart {
    private PaymentStrategy $paymentStrategy;

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

    public function checkout(float $totalAmount): string {
        if (!isset($this->paymentStrategy)) {
            throw new RuntimeException('Payment strategy not set.');
        }
        return $this->paymentStrategy->pay($totalAmount);
    }
}

// Использование
$cart = new ShoppingCart();
$cart->setPaymentStrategy(new CreditCardPayment('4111111111111111', '123'));
echo $cart->checkout(149.99) . "n";
// Paid 149.99 using Credit Card ending with 1111

$cart->setPaymentStrategy(new PayPalPayment('user@example.com'));
echo $cart->checkout(89.50) . "n";
// Paid 89.5 using PayPal account: user@example.com

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

  • Изоляция кода алгоритма: Каждая стратегия — отдельный класс, который легко тестировать.
  • Избегание условных операторов: Контекст не содержит логики выбора алгоритма.
  • Взаимозаменяемость: Стратегии можно менять на лету.
  • Соответствует принципу открытости/закрытости: Новые алгоритмы добавляются без изменения контекста.