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

Ответ

Паттерн 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

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

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

Ответ 18+ 🔞

Слушай, а вот паттерн Strategy — это вообще огонь, если ты задолбался этими бесконечными if и switch на триста веток. Представь, у тебя есть какая-то логика, которая может выполняться по-разному. Вместо того чтобы городить говнокод с условиями, ты просто выносишь каждый вариант в отдельный класс-стратегию. Получается семейство алгоритмов, и ты можешь их подменять, как перчатки, — ёпта, красота же!

Суть в чём: Ты определяешь общий интерфейс для всех алгоритмов, а потом каждый конкретный алгоритм (стратегию) реализуешь в своём классе. Контекст (тот, кто использует алгоритм) работает с этим интерфейсом и ему да похуй, какая именно стратегия ему подсунута. Главное, чтобы у неё был нужный метод. В итоге вместо монструозной процедуры с ветвлениями получаешь чистый полиморфизм — охуенно, правда?

Смотри на примере системы оплаты, тут всё понятно станет:

// Вот наш главный договор — интерфейс стратегии. Все, кто хочет быть стратегией, должны уметь платить.
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);
    }
}

// А это оплата через PayPal. Совсем другой алгоритм, но тот же интерфейс.
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;

    // Подсовываем ей любую стратегию на лету. Вжух — и оплата картой, вжух — и PayPal.
    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.');
        }
        // Корзина вызывает pay(), а как именно он работает — её не ебёт.
        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

// Передумали — переключились на PayPal. Ничего не ломается.
$cart->setPaymentStrategy(new PayPalPayment('user@example.com'));
echo $cart->checkout(89.50) . "n";
// Выведет: Paid 89.5 using PayPal account: user@example.com

В чём, блядь, профит?

  • Код алгоритмов изолирован. Каждая стратегия — отдельный класс. Хочешь протестировать оплату криптой? Тестируй только её, не трогая всю корзину. Волнение ебать от такой простоты.
  • Нет этих ебучих условных операторов. Не нужно в checkout() писать if ($method == 'card') {...} elseif ($method == 'paypal') {...}. Контекст чистый, как слеза младенца.
  • Взаимозаменяемость на ходу. Стратегию можно поменять прямо перед вызовом, как в примере выше.
  • Соответствует принципу открытости/закрытости. Надо добавить оплату через СБП? Создаёшь новый класс SbpPayment, реализуешь интерфейс — и всё, блядь. Контекст (ShoppingCart) менять не нужно. Это же ни хуя себе удобно!

Короче, если видишь, что у тебя одна и та же операция делается по-разному в зависимости от какого-то условия — это крик о помощи и намёк, что пора заюзать Strategy.