Ответ
Принцип единственной ответственности (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 применим не только к классам, но и к методам (метод должен делать одну вещь) и даже к модулям в целом.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶