Принцип единственной ответственности гласит:

У каждого класса должна быть только одна причина для изменения.

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


❌ Нарушение принципа (плохой код)

В этом примере один класс OrderManager делает абсолютно всё: считает стоимость, имитирует сохранение в базу данных, отправляет email и пишет логи.

class OrderManager {
  public createOrder(orderId: string, items: any[]) {
    // 1. Бизнес-логика: подсчет стоимости
    const total = items.reduce((sum, item) => sum + item.price, 0);
    console.log(`Заказ ${orderId} создан. Сумма: ${total} руб.`);

    // 2. Нарушение SRP: Работа с базой данных
    console.log(`Сохранение заказа ${orderId} в базу данных...`);

    // 3. Нарушение SRP: Отправка уведомлений
    console.log(`Отправка Email: Ваш заказ ${orderId} успешно оформлен!`);

    // 4. Нарушение SRP: Логирование
    console.log(`[LOG] Заказ ${orderId} обработан в ${new Date().toISOString()}`);
  }
}

// Использование:
const manager = new OrderManager();
manager.createOrder("101", [{ price: 500 }, { price: 1500 }]);

✅ Соблюдение принципа (хороший код)

Мы разделяем обязанности. Создаем отдельные специализированные классы для базы данных, уведомлений и логирования. Класс OrderService теперь отвечает только за управление процессом (бизнес-логику).

1. Выносим отдельные ответственности в свои классы

// Класс отвечает ТОЛЬКО за сохранение данных
class OrderRepository {
  public save(orderId: string): void {
    console.log(`Сохранение заказа ${orderId} в базу данных...`);
  }
}

// Класс отвечает ТОЛЬКО за отправку сообщений
class EmailNotification {
  public sendConfirmation(orderId: string): void {
    console.log(`Отправка Email: Ваш заказ ${orderId} успешно оформлен!`);
  }
}

// Класс отвечает ТОЛЬКО за логирование
class Logger {
  public info(message: string): void {
    console.log(`[LOG] ${message} в ${new Date().toISOString()}`);
  }
}

2. Собираем всё в главном сервисе управления заказами

Теперь OrderService получает нужные инструменты через конструктор и просто координирует их работу.

class OrderService {
  constructor(
    private repository: OrderRepository,
    private notification: EmailNotification,
    private logger: Logger
  ) {}

  public createOrder(orderId: string, items: any[]): void {
    // Чистая бизнес-логика (расчет стоимости)
    const total = items.reduce((sum, item) => sum + item.price, 0);
    console.log(`Заказ ${orderId} создан. Сумма: ${total} руб.`);

    // Делегируем задачи другим классам
    this.repository.save(orderId);
    this.notification.sendConfirmation(orderId);
    this.logger.info(`Заказ ${orderId} успешно обработан`);
  }
}

// Использование:
// 1. Создаем экземпляры вспомогательных классов
const repo = new OrderRepository();
const mailer = new EmailNotification();
const logger = new Logger();

// 2. Передаем их в главный сервис (Внедрение зависимостей вручную)
const orderService = new OrderService(repo, mailer, logger);
orderService.createOrder("102", [{ price: 300 }, { price: 700 }]);

В чем выгода такого разделения?

  1. Легко читать: Каждый класс маленький, его код понятен с первого взгляда.
  2. Легко менять: Если завтра вместо Email нужно будет отправлять SMS, мы изменим только класс EmailNotification (или заменим его на SmsNotification), а код подсчета стоимости заказов останется нетронутым.
  3. Повторное использование: Класс Logger или EmailNotification теперь можно использовать в других частях программы (например, при регистрации пользователей), не копируя код.