Ответ
Для реализации гибкой системы скидок, зависящей от типа пользователя, я бы применил паттерн Стратегия (Strategy). Это позволяет инкапсулировать различные алгоритмы расчёта и выбирать их во время выполнения.
1. Определяем интерфейс стратегии:
// src/Service/Discount/Strategy/DiscountStrategyInterface.php
interface DiscountStrategyInterface
{
public function calculate(float $originalAmount): float;
}
2. Реализуем конкретные стратегии:
// src/Service/Discount/Strategy/RegularCustomerDiscountStrategy.php
class RegularCustomerDiscountStrategy implements DiscountStrategyInterface
{
public function calculate(float $originalAmount): float
{
// Скидка 5% для обычных клиентов
return $originalAmount * 0.95;
}
}
// src/Service/Discount/Strategy/VipCustomerDiscountStrategy.php
class VipCustomerDiscountStrategy implements DiscountStrategyInterface
{
public function calculate(float $originalAmount): float
{
// Скидка 15% для VIP
return $originalAmount * 0.85;
}
}
// src/Service/Discount/Strategy/NewYearDiscountStrategy.php
class NewYearDiscountStrategy implements DiscountStrategyInterface
{
public function calculate(float $originalAmount): float
{
// Новогодняя скидка 10% всем
return $originalAmount * 0.90;
}
}
3. Создаём контекст (сервис), который использует стратегию:
// src/Service/Discount/DiscountCalculator.php
class DiscountCalculator
{
private DiscountStrategyInterface $strategy;
// Стратегия внедряется через конструктор (Dependency Injection)
public function __construct(DiscountStrategyInterface $strategy)
{
$this->strategy = $strategy;
}
// Метод для смены стратегии на лету (опционально)
public function setStrategy(DiscountStrategyInterface $strategy): void
{
$this->strategy = $strategy;
}
public function calculate(float $amount): float
{
// Делегируем расчёт текущей стратегии
return $this->strategy->calculate($amount);
}
}
4. Использование в коде приложения:
// Определяем тип пользователя (например, из БД или сессии)
$userType = $user->getType(); // 'vip', 'regular'
// Выбираем стратегию на основе типа пользователя
// Это можно вынести в фабрику или конфигурацию DI-контейнера
switch ($userType) {
case 'vip':
$strategy = new VipCustomerDiscountStrategy();
break;
case 'seasonal':
$strategy = new NewYearDiscountStrategy();
break;
default:
$strategy = new RegularCustomerDiscountStrategy();
}
// Создаём калькулятор с нужной стратегией
$discountCalculator = new DiscountCalculator($strategy);
$finalPrice = $discountCalculator->calculate(1000); // 850 для VIP
Преимущества подхода:
- Открыто/закрыто: Легко добавить новую стратегию скидок (
BlackFridayDiscountStrategy), не меняя код калькулятора или других стратегий. - Чистота кода: Каждый алгоритм в отдельном классе.
- Тестируемость: Стратегии можно тестировать изолированно.