Ответ
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можно использовать в других контекстах (регистрация пользователя, сброс пароля). - Читаемость: Код становится предсказуемым, так как каждый модуль делает ровно одну понятную вещь.
Ответ 18+ 🔞
А, слушай, про этот ваш SRP, ёпта. Ну, принцип-то вроде и простой, а народ его так изнасиловать может, что мама не горюй. Типа, один класс — одна причина для изменений. А на деле выходит, что один класс делает всё: и в базу пишет, и письма шлёт, и счета генерит, и ещё, наверное, кофе тебе сварит, если попросить. Ебать мои старые костыли, ну и манда с ушами получается, а не класс.
Смотри, вот тебе живой пример, как делать НЕ НАДО. Представь себе модуль OrderProcessor. Чувак, который внутри творит такую дичь, что удивление пиздец.
// Плохо: Модуль делает слишком много
class OrderProcessor {
constructor(order) {
this.order = order;
}
validate() { /* логика валидации */ }
calculateTotal() { /* логика расчёта */ }
saveToDatabase() { /* логика работы с БД */ }
sendConfirmationEmail() { /* логика отправки email */ }
generateInvoicePDF() { /* логика генерации PDF */ }
}
Представил? Вот этот уродец. Он у тебя изменится по пяти разным причинам! Захотел ты поменять библиотеку для PDF — трогай этот класс. Почтовик сменил API — опять лезь сюда. Схему в базе поправил — и снова ты тут. Волнение ебать начинается, потому что одно изменение в одном месте может нечаянно сломать всё остальное. Доверия ебать ноль к такому коду.
А теперь смотри, как надо бы, по-человечески. Разделяем эту пиздопроебибну на нормальные, вменяемые куски.
// Хорошо: Ответственности разделены
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;
}
}
Видишь разницу? Каждый мелкий сервис — хитрая жопа, которая знает свой один маленький трюк и делает его хорошо. А главный OrderService просто как дирижёр, всех этих умельцев собирает и говорит: "Работайте, суки!"
И практическая-то польза охуенная:
- Тестируемость: Каждого ушлёпка можно тестировать отдельно. Хочешь проверить
EmailService? Подсуни ему мок заказа и смотри, шлёт он письма или нет. Ёперный театр, да это же мечта! - Поддержка: Поменял что-то в логике отправки писем? Ну и хуй с ним, меняй только
EmailService. Валидация заказов, генерация PDF — они об этом даже не узнают, живут своей счастливой жизнью. - Переиспользование: Этот самый
EmailServiceможно потом тырить для регистрации пользователей или сброса пароля. Один раз написал — пользуйся где хочешь. Красота. - Читаемость: Открываешь любой класс и сразу видно — а, этот чувак только валидирует. А этот — только считает. Никаких сюрпризов. Э бошка думай меньше надо, когда ищешь баг.
Вот и весь принцип. Не делай из класса универсальную давалку во все дыры. Пусть каждый занимается своим делом, и в проекте будет порядок, а не ебушки-воробушки.