Single Responsibility Principle
Single Responsibility Principle (SRP) — первый принцип SOLID: каждый класс, функция или модуль должны иметь только одну причину для изменения, то есть отвечать за одну конкретную задачу.
Зачем нужно
Нарушение SRP приводит к «God objects» — классам, которые знают всё и делают всё. Такой код сложно тестировать (много зависимостей), изменять (риск сломать несвязанный функционал) и читать. SRP позволяет изолировать изменения: правки в логике email-рассылки не затрагивают работу с базой данных.
Где используется
- Разделение слоёв: data access, business logic, presentation
- Выделение сервисов: UserService, EmailService, ReportService
- Функции: каждая делает одно и хорошо (Unix philosophy)
- React-компоненты: UI отдельно от логики (hooks, сервисы)
Основной контент
Нарушение SRP
// Плохо: класс делает несколько несвязанных вещей
class UserManager {
constructor(db, mailer) {
this.db = db;
this.mailer = mailer;
}
createUser(userData) {
// Валидация
if (!userData.email) throw new Error('Email обязателен');
if (userData.password.length < 8) throw new Error('Пароль слишком короткий');
// Сохранение в БД
const user = this.db.insert('users', userData);
// Отправка письма
this.mailer.send(user.email, 'Добро пожаловать!');
// Генерация отчёта
this.db.insert('audit_log', { action: 'user_created', userId: user.id });
return user;
}
}
// Изменится формат email → меняем UserManager
// Изменится структура БД → меняем UserManager
// Изменится шаблон письма → меняем UserManager
Соблюдение SRP
// Хорошо: каждый класс — одна ответственность
class UserValidator {
validate(userData) {
if (!userData.email) throw new Error('Email обязателен');
if (userData.password.length < 8) throw new Error('Пароль слишком короткий');
}
}
class UserRepository {
constructor(db) { this.db = db; }
save(userData) { return this.db.insert('users', userData); }
}
class EmailService {
constructor(mailer) { this.mailer = mailer; }
sendWelcome(email) {
return this.mailer.send(email, 'Добро пожаловать!');
}
}
class AuditLogger {
constructor(db) { this.db = db; }
log(action, meta) { this.db.insert('audit_log', { action, ...meta }); }
}
// Координатор — thin controller
class UserRegistrationService {
constructor(validator, repository, emailService, auditLogger) {
this.validator = validator;
this.repository = repository;
this.emailService = emailService;
this.auditLogger = auditLogger;
}
async register(userData) {
this.validator.validate(userData);
const user = await this.repository.save(userData);
await this.emailService.sendWelcome(user.email);
this.auditLogger.log('user_created', { userId: user.id });
return user;
}
}
SRP для функций
// Плохо: функция делает несколько вещей
function processOrderData(order) {
// валидация
if (!order.items.length) throw new Error('Нет товаров');
// форматирование
const total = order.items.reduce((s, i) => s + i.price * i.qty, 0);
const formatted = { ...order, total, date: new Date.toISOString() };
// сохранение
localStorage.setItem('lastOrder', JSON.stringify(formatted));
return formatted;
}
// Хорошо: разделить
const validateOrder = order => {
if (!order.items.length) throw new Error('Нет товаров');
};
const calculateTotal = items =>
items.reduce((s, i) => s + i.price * i.qty, 0);
const formatOrder = order => ({
...order,
total: calculateTotal(order.items),
date: new Date.toISOString()
});
const saveOrder = order =>
localStorage.setItem('lastOrder', JSON.stringify(order));
Частые ошибки
- Слишком узкая трактовка — дробить код до одной строки на класс — избыточно. «Одна причина для изменения» не означает «одна строка». Группируйте связанное.
- Нарушение SRP через «utilities» —
helpers.jsилиutils.jsс 50 несвязанными функциями — нарушение. Разбивайте по доменам. - Игнорирование SRP в функциях — принцип применим не только к классам, но и к функциям. Функция, которая и валидирует, и сохраняет, и отправляет — нарушает SRP.
🎓 Источники
- 🎓 [SOLID SRP — Single Responsibility Principle for JavaScript] · 2024-11-29 · YouTube
- Тезисы: SRP близок к Separation of Concerns. Применим не только к ООП — к функциям, модулям, процедурам тоже. Где остановиться дробление — только интуиция инженера, тренируется опытом.
- Альтернативная позиция: «Single responsibility очень похож на общий инженерный принцип separation of concerns. Все это про то, насколько гранулярно мы хотим писать код». SRP не имеет точного критерия — только опыт.
- 🎓 [The Single Responsibility Principle and Conway's Law] · 2019-10-17 · YouTube
- Тезисы: SRP — это принцип, а не закон. Дробление увеличивает количество классов и объём кода — это его цена. Захардкоженный конфиг внутри класса ломает SRP. Связан с законом Конвея: структура кода отражает структуру команды.