Jeer
@Jeer
уверенный пользователь

Как можно отрефакторить код?

Всем привет,
Вопрос скорее про паттерны, без опыта не понятно, как их тут можно применить
Есть не сложные модели (рекордами)
public record Cart(int DeliveryZoneId, string? Promocode, int[] ProductIds);
public record DeliveryZone(int Id, double Fee);
public record Product(int Id, double Price, string Name);
public record Promocode(double Discount, string Code);

Собственно, в корзине имеется зона доставки, может быть промокод и список каких-то айдишников продуктов
Нужно посчитать по корзине итоговую сумму, учитывая скидку за промокод, учитывая минимальную сумму заказа (если она меньше, то добавляется) и есть еще момент, что если много товаров в корзине, то добавляется еще какая-то сумма за большой заказ.
Вот такой код у меня получился, он простой, как топор, и я не понимаю, как его можно еще улучшить. Тут напрашиваются паттерны цепочка обязанностей и стратегия. Я читаю по ним инфу и мне кажется, они тут не нужны (примеры может плохие). С другой стороны, если я пытаюсь что-то добавить, то получается слишком сильное усложнение, новые сущности, не сразу очевидные вещи. Короч, помогите разобраться, вот мой вариант задачи:
using CartCalculator.Models;

namespace CartCalculator;

public class Calculator
{
    private const double FreeDeliverySum = 1000;
    private const int VeryLargeOrderProductCount = 20;
    private const double VeryLargeOrderFee = 200;

    private readonly Product[] _allMenuProducts;
    private readonly Promocode[] _allAvailablePromocodes;
    private readonly DeliveryZone[] _allDeliveryZones;

    public Calculator(Product[] allMenuProducts, Promocode[] allAvailablePromocodes, DeliveryZone[] allDeliveryZones)
    {
        _allMenuProducts = allMenuProducts;
        _allAvailablePromocodes = allAvailablePromocodes;
        _allDeliveryZones = allDeliveryZones;
    }

    public double CalculateTotalPrice(Cart cart)
    {
        var totalPrice = TotalPrice(cart.ProductIds);

        if (!string.IsNullOrEmpty(cart.Promocode))
        {
            var discount = GetPromocodeDiscount(cart.Promocode);
            totalPrice *= 1 - discount;
        }

        if (totalPrice < FreeDeliverySum)
        {
            var deliveryFee = GetDeliveryFee(cart.DeliveryZoneId);
            totalPrice += deliveryFee;
        }

        if (cart.ProductIds.Length > VeryLargeOrderProductCount)
        {
            totalPrice += VeryLargeOrderFee;
        }

        return totalPrice;
    }

    private double GetDeliveryFee(int deliveryZoneId)
    {
        var deliveryZone = _allDeliveryZones.SingleOrDefault(dz => dz.Id == deliveryZoneId);

        if (deliveryZone == null)
        {
            throw new Exception($"Cannot find delivery zone with id \"{deliveryZoneId}\"");
        }

        return deliveryZone.Fee;
    }

    private double GetPromocodeDiscount(string promocode)
    {
        var promo = _allAvailablePromocodes.SingleOrDefault(p => p.Code == promocode);

        if (promo == null)
        {
            throw new Exception($"Cannot find promocode \"{promocode}\"");
        }

        return promo.Discount;
    }

    private double TotalPrice(IEnumerable<int> productIds)
    {
        double totalPrice = 0;

        foreach (var productId in productIds)
        {
            var product = _allMenuProducts.SingleOrDefault(p => p.Id == productId);

            if (product == null)
            {
                throw new Exception($"Cannot find product with id \"${productId}\"");
            }

            totalPrice += product.Price;
        }

        return totalPrice;
    }
}
  • Вопрос задан
  • 82 просмотра
Решения вопроса 1
Jeer
@Jeer Автор вопроса
уверенный пользователь
Что-то никто так и не ответил. В общем, нам нужно сделать некий обобщенный интерфейс и под него подтянуть наши шаги, которые потом можно вынести отдельно. Получится что-то такое:
using CartCalculator.Models;

namespace CartCalculator;

public class Calculator
{
    private readonly Product[] _allMenuProducts;
    private readonly Promocode[] _allAvailablePromocodes;
    private readonly DeliveryZone[] _allDeliveryZones;

    public Calculator(Product[] allMenuProducts, Promocode[] allAvailablePromocodes, DeliveryZone[] allDeliveryZones)
    {
        _allMenuProducts = allMenuProducts;
        _allAvailablePromocodes = allAvailablePromocodes;
        _allDeliveryZones = allDeliveryZones;
    }

    public double CalculateTotalPrice(Cart cart)
    {
        double totalPrice = 0;

        ICalculationStep[] steps =
        {
            new ProductsSumStep(_allMenuProducts),
            new PromoCodeStep(_allAvailablePromocodes),
            new DeliveryFeeStep(_allDeliveryZones),
        };

        foreach (var step in steps)
        {
            totalPrice = step.Calculate(cart, totalPrice);
        }

        return totalPrice;
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы