Что такое принцип единственной ответственности (Single Responsibility Principle, SRP)?

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

Ответ

Принцип единственной ответственности (SRP) — первый и один из ключевых принципов SOLID. Его формулировка: класс должен иметь одну и только одну причину для изменения.

Что это значит на практике? Класс должен быть ответственен за одну узкую часть функциональности (задачу, аспект). Если у класса несколько несвязанных обязанностей, то изменения в требованиях к одной из них будут вынуждать менять этот класс, рискуя сломать другую.

Пример класса, нарушающего SRP:

public class OrderProcessor
{
    // Ответственность 1: Валидация заказа
    public bool Validate(Order order) { /* ... */ }

    // Ответственность 2: Сохранение в БД
    public void SaveToDatabase(Order order) { /* ... */ }

    // Ответственность 3: Генерация чека
    public void GenerateInvoice(Order order) { /* ... */ }

    // Ответственность 4: Отправка уведомления
    public void SendEmailConfirmation(Order order) { /* ... */ }
}

Такой "божественный объект" (God Object) сложно тестировать, повторно использовать и поддерживать. Изменение формата email затронет класс, отвечающий за логику заказов.

Рефакторинг с соблюдением SRP:

// Класс отвечает только за бизнес-логику обработки заказа
public class OrderService
{
    private readonly IOrderValidator _validator;
    private readonly IOrderRepository _repository;
    private readonly IInvoiceGenerator _invoiceGenerator;
    private readonly INotificationService _notificationService;

    // Зависимости внедряются через конструктор (Dependency Injection)
    public OrderService(IOrderValidator validator,
                        IOrderRepository repository,
                        IInvoiceGenerator invoiceGenerator,
                        INotificationService notificationService)
    {
        _validator = validator;
        _repository = repository;
        _invoiceGenerator = invoiceGenerator;
        _notificationService = notificationService;
    }

    public void ProcessOrder(Order order)
    {
        if (!_validator.Validate(order))
            throw new ValidationException("Invalid order");

        _repository.Save(order);
        _invoiceGenerator.Generate(order);
        _notificationService.SendConfirmation(order);
    }
}

// Каждый сервис отвечает за свою узкую задачу
public interface IOrderValidator { bool Validate(Order order); }
public interface IOrderRepository { void Save(Order order); }
public interface IInvoiceGenerator { void Generate(Order order); }
public interface INotificationService { void SendConfirmation(Order order); }

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

  • Упрощение тестирования: Каждый маленький класс проще покрыть unit-тестами.
  • Повышение переиспользуемости: Класс EmailNotificationService можно использовать не только для заказов.
  • Снижение связанности: Изменения в логике отправки email изолированы в одном классе.
  • Улучшение читаемости: Код организован по четким, понятным модулям.

SRP применим не только к классам, но и к методам (метод должен делать одну вещь) и даже к модулям в целом.

Видео-ответы