← Назад к вопросам

Применял ли Strategy?

1.0 Junior🔥 121 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, я применял паттерн Стратегия (Strategy) многократно в различных проектах. Это один из наиболее часто используемых поведенческих паттернов в моей практике, особенно при проектировании гибких и расширяемых backend-систем на PHP. Его основная идея — инкапсуляция семейства алгоритмов, их взаимозаменяемость и независимость от клиентского кода — идеально подходит для сценариев, где необходимо варьировать поведение в зависимости от контекста, конфигурации или пользовательского ввода.

Конкретные примеры применения

1. Обработка платежей

В e-commerce проектах интеграция с несколькими платежными шлюзами (Stripe, PayPal, CloudPayments) — классический случай для Strategy.

<?php

interface PaymentStrategy
{
    public function pay(float $amount): bool;
}

class StripePaymentStrategy implements PaymentStrategy
{
    public function pay(float $amount): bool
    {
        // Логика вызова API Stripe
        echo "Оплата {$amount} через Stripe\n";
        return true;
    }
}

class PayPalPaymentStrategy implements PaymentStrategy
{
    public function pay(float $amount): bool
    {
        // Логика вызова API PayPal
        echo "Оплата {$amount} через PayPal\n";
        return true;
    }
}

class PaymentContext
{
    private PaymentStrategy $strategy;
    
    public function __construct(PaymentStrategy $strategy)
    {
        $this->strategy = $strategy;
    }
    
    public function setStrategy(PaymentStrategy $strategy): void
    {
        $this->strategy = $strategy;
    }
    
    public function executePayment(float $amount): bool
    {
        return $this->strategy->pay($amount);
    }
}

// Использование
$payment = new PaymentContext(new StripePaymentStrategy());
$payment->executePayment(100.50);

$payment->setStrategy(new PayPalPaymentStrategy());
$payment->executePayment(75.25);

Преимущества в этом сценарии:

  • Легкое добавление новых платежных систем без изменения существующего кода обработки заказов
  • Возможность динамической смены стратегии (например, при ошибке одного шлюза)
  • Упрощение юнит-тестирования за счет изолированных стратегий

2. Экспорт данных в различные форматы

Системы отчетности часто требуют выгрузки в PDF, Excel, CSV. Strategy идеально разделяет логику генерации.

<?php

interface ExportStrategy
{
    public function export(array $data): string;
}

class CsvExportStrategy implements ExportStrategy
{
    public function export(array $data): string
    {
        // Генерация CSV
        return "CSV data";
    }
}

class PdfExportStrategy implements ExportStrategy
{
    public function export(array $data): string
    {
        // Генерация PDF через Dompdf/TCPDF
        return "PDF content";
    }
}

class ReportGenerator
{
    public function generate(ExportStrategy $exporter, array $data): string
    {
        // Общая подготовка данных
        $processedData = $this->processData($data);
        
        // Делегирование экспорта стратегии
        return $exporter->export($processedData);
    }
}

3. Система кэширования с разными драйверами

Выбор между Redis, Memcached, файловым кэшем в зависимости от окружения.

<?php

interface CacheStrategy
{
    public function get(string $key): mixed;
    public function set(string $key, mixed $value, int $ttl): void;
}

class RedisCacheStrategy implements CacheStrategy { /* ... */ }
class FileCacheStrategy implements CacheStrategy { /* ... */ }

// Конфигурирование стратегии на основе environment
$cacheStrategy = $_ENV['CACHE_DRIVER'] === 'redis' 
    ? new RedisCacheStrategy() 
    : new FileCacheStrategy();
    
$cacheService = new CacheService($cacheStrategy);

Практические преимущества, которые я наблюдал

  • Снижение связанности: Клиентский код зависит от абстракции (интерфейса), а не от конкретных реализаций
  • Принцип открытости/закрытости: Новые стратегии добавляются без модификации существующего контекста
  • Улучшение тестируемости: Стратегии можно тестировать изолированно, легко создавать моки
  • Упрощение рефакторинга: Когда появляется новый алгоритм или меняется внешний API (например, платежной системы), изменения локализованы в одном классе
  • Повышение читаемости: Убирает "лапшу" из условных операторов (if/switch) при выборе алгоритмов

Нюансы реализации в PHP

  • Выбор между внедрением зависимости и фабрикой: В простых случаях стратегия инжектится через конструктор, в сложных — используется фабрика стратегий для создания экземпляров по условию
  • Ламповость с Laravel Service Container: В Laravel стратегии удобно биндить к интерфейсам в провайдерах:
$this->app->bind(PaymentStrategy::class, function ($app) {
    return config('payment.default') === 'stripe'
        ? new StripePaymentStrategy()
        : new PayPalPaymentStrategy();
});
  • Состояние стратегий: Обычно стратегии делаются без состояния (stateless), но иногда необходимо передавать контекстные данные через параметры методов
  • Производительность: Создание объектов стратегий "на лету" в горячих путях может создать overhead, в таких случаях используют кэширование или пулы объектов

Альтернативы и когда Strategy не подходит

Pattern Strategy не всегда оптимален:

  • Если вариаций поведения мало (1-2), возможно, проще использовать условные операторы
  • Когда алгоритмы сильно различаются по набору параметров (приходится создавать "раздутые" интерфейсы)
  • В случаях, где более уместен паттерн Состояние (State) (когда поведение меняется в зависимости от внутреннего состояния объекта)

В моей практике Strategy особенно ценен в долгоживущих проектах, где требования к вариативности поведения со временем растут. Он создает архитектурный "каркас", который позволяет системе эволюционировать с минимальными изменениями в основных бизнес-процессах. Этот паттерн стал для меня таким же базовым инструментом, как использование интерфейсов или внедрение зависимостей.

Применял ли Strategy? | PrepBro