Какие подходы и принципы вы реализовали в созданном вами классе?

«Какие подходы и принципы вы реализовали в созданном вами классе?» — вопрос из категории ООП, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При проектировании классов руководствуюсь принципами SOLID и отдаю предпочтение композиции перед наследованием. Вот конкретные подходы на примере класса OrderProcessor:

  1. Принцип единственной ответственности (SRP): Класс отвечает только за координацию процесса обработки заказа, а не за логику оплаты или логирования.
  2. Принцип открытости/закрытости (OCP): Класс открыт для расширения (можно подставить любую реализацию IPaymentGateway), но закрыт для модификации.
  3. Принцип инверсии зависимостей (DIP): Класс зависит от абстракций (IPaymentGateway, ILogger), а не от конкретных реализаций.
  4. Внедрение зависимостей (DI): Зависимости передаются через конструктор, что упрощает тестирование и управление жизненным циклом объектов.
  5. Композиция: Функциональность собрана из независимых компонентов (шлюз оплаты, логгер), а не унаследована от базового класса.
  6. Инкапсуляция: Внутреннее состояние (зависимости) и детали реализации скрыты, предоставлен четкий публичный метод ProcessOrder.

Пример реализации:

public class OrderProcessor
{
    private readonly IPaymentGateway _paymentGateway;
    private readonly ILogger _logger;

    // Внедрение зависимостей через конструктор
    public OrderProcessor(IPaymentGateway paymentGateway, ILogger logger)
    {
        _paymentGateway = paymentGateway ?? throw new ArgumentNullException(nameof(paymentGateway));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    // Единственная публичная ответственность класса
    public async Task<ProcessResult> ProcessOrder(Order order)
    {
        // Валидация входных данных (Guard Clauses)
        if (order == null) throw new ArgumentNullException(nameof(order));
        if (order.Total <= 0) throw new InvalidOrderException("Order total must be positive.");

        try
        {
            // Делегирование ответственности специализированным сервисам
            var paymentResult = await _paymentGateway.ProcessPaymentAsync(order);
            _logger.LogInformation($"Order {order.Id} processed successfully. Payment ID: {paymentResult.Id}");
            return ProcessResult.Success(paymentResult.Id);
        }
        catch (PaymentGatewayException ex) // Ловим только специфичные исключения
        {
            _logger.LogError(ex, $"Failed to process payment for order {order.Id}");
            // Преобразуем низкоуровневое исключение в исключение предметной области
            throw new OrderProcessingException("Payment failed", ex);
        }
    }
}

Итог: Такой подход делает класс тестируемым (легко подменить IPaymentGateway моком), поддерживаемым и готовым к интеграции в современные DI-контейнеры.