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

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

Ответ

Single Responsibility Principle (SRP) — первый принцип SOLID, который утверждает, что класс, модуль или функция должны иметь одну и только одну причину для изменения. В контексте Node.js это часто относится к модулям, сервисам или функциям.

Пример нарушения SRP в Node.js-модуле:

// Плохо: Модуль делает слишком много
class OrderProcessor {
  constructor(order) {
    this.order = order;
  }

  validate() { /* логика валидации */ }
  calculateTotal() { /* логика расчёта */ }
  saveToDatabase() { /* логика работы с БД */ }
  sendConfirmationEmail() { /* логика отправки email */ }
  generateInvoicePDF() { /* логика генерации PDF */ }
}

Такой класс изменится, если поменяется логика валидации, расчётов, схема БД, почтовый сервис или библиотека для PDF.

Пример c соблюдением SRP:

// Хорошо: Ответственности разделены
class OrderValidator {
  validate(order) { /* только валидация */ }
}

class OrderCalculator {
  calculateTotal(order) { /* только расчёт */ }
}

class OrderRepository {
  async save(order) { /* только сохранение в БД */ }
}

class EmailService {
  async sendConfirmation(order) { /* только отправка email */ }
}

class InvoiceService {
  async generate(order) { /* только генерация PDF */ }
}

// Композиция в основном сервисе
class OrderService {
  constructor(validator, calculator, repository, emailService, invoiceService) {
    this.validator = validator;
    this.calculator = calculator;
    this.repository = repository;
    this.emailService = emailService;
    this.invoiceService = invoiceService;
  }

  async process(order) {
    this.validator.validate(order);
    order.total = this.calculator.calculateTotal(order);
    await this.repository.save(order);
    await this.emailService.sendConfirmation(order);
    await this.invoiceService.generate(order);
    return order;
  }
}

Практическая польза в Node.js:

  • Тестируемость: Каждый сервис можно протестировать изолированно с помощью моков (например, jest).
  • Поддержка: Изменение в логике отправки email не затронет код валидации заказа.
  • Переиспользование: EmailService можно использовать в других контекстах (регистрация пользователя, сброс пароля).
  • Читаемость: Код становится предсказуемым, так как каждый модуль делает ровно одну понятную вещь.