Принцип единственной ответственности гласит:
У каждого класса должна быть только одна причина для изменения.
Это означает, что класс должен решать ровно одну конкретную задачу. Если класс делает слишком много разнородных вещей (работает с базой данных, шлет письма, форматирует текст), его становится тяжело тестировать, изменять и поддерживать. Изменение одной функции может случайно сломать другую.
❌ Нарушение принципа (плохой код)
В этом примере один класс 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 }]);В чем выгода такого разделения?
- Легко читать: Каждый класс маленький, его код понятен с первого взгляда.
- Легко менять: Если завтра вместо Email нужно будет отправлять SMS, мы изменим только класс
EmailNotification(или заменим его наSmsNotification), а код подсчета стоимости заказов останется нетронутым. - Повторное использование: Класс
LoggerилиEmailNotificationтеперь можно использовать в других частях программы (например, при регистрации пользователей), не копируя код.