Принцип единой ответственности
Далее: SRP (single-responsibility principle)
Начнём с самого простого и одновременно самого часто нарушаемого принципа.
Принцип единственной ответственности звучит так:
У сущности должна быть только одна причина для изменения.
Звучит просто, да? Но на практике мы постоянно пишем классы, которые делают всё и сразу. А потом удивляемся, почему так сложно что-то поменять.
Пример из жизни
Представь, что ты работаешь в офисе. Твоя задача — писать код. Но вдруг начальник говорит:
Слушай, а можешь ещё и документы подписывать, кофе всем носить, и заодно презентации делать?
Ты бы, наверное, офигел. Потому что это не твоя зона ответственности. Ты — программист, а не бариста и не секретарь. У тебя одна работа, за которую ты отвечаешь.
Так же и с классами.
Плохой пример
Вот класс, который нарушает SRP:
export class UserService {
public getUser(id: number) {
/* ... */
}
public updateUser(id: number, data: any) {
/* ... */
}
public sendWelcomeEmail(email: string) {
/* ... */
}
public sendNotification(email: string, message: string) {
/* ... */
}
public logToFile(message: string) {
/* ... */
}
public logToDatabase(message: string) {
/* ... */
}
}Что здесь не так? Всё! Этот класс:
- Управляет пользователями
- Отправляет письма
- Пишет логи
У него три причины для изменения. Если поменяется логика отправки писем — мы трогаем UserService.
Если поменяется формат логов — опять трогаем этот же класс. И так по кругу.
Такой класс быстро превращается в “свалку” методов. Его невозможно тестировать, сложно читать, а уж поддерживать — просто кошмар.
Хороший пример
Разделим ответственность по разным классам:
export class UserService {
public getUser(id: number) {
/* ... */
}
public updateUser(id: number, data: any) {
/* ... */
}
}
export class EmailService {
public sendWelcomeEmail(email: string) {
/* ... */
}
public sendNotification(email: string, message: string) {
/* ... */
}
}
export class LoggerService {
public logToFile(message: string) {
/* ... */
}
public logToDatabase(message: string) {
/* ... */
}
}Теперь:
- Хочешь поменять логику писем? Иди в
EmailService. - Нужно добавить новый способ логирования? Добро пожаловать в
LoggerService. - Что-то с пользователями? Работаем только с
UserService.
У каждого класса — одна ответственность, одна причина для изменения.
Как понять, что ты нарушаешь SRP?
Задай себе простой вопрос:
Если я изменю это место, сколько всего другого мне придётся перепроверить?
Если больше одного модуля — скорее всего, ты нарушил SRP.
Ещё один признак: если ты не можешь описать, что делает класс, одним предложением без “и”, “а также”, “к тому же” — это звоночек.
“Этот класс управляет пользователями, и отправляет письма, и логирует”
“Этот класс управляет пользователями”
Итог
SRP — это про границы. Про то, чтобы каждый кусок кода занимался своим делом и не лез в чужие.
На первый взгляд кажется, что больше классов = больше кода = сложнее. Но на практике наоборот: код становится проще, понятнее и надёжнее.