Ответ
SOLID — это пять принципов объектно-ориентированного проектирования, улучшающих читаемость, гибкость и поддерживаемость кода.
- Single Responsibility (SRP) — класс должен иметь одну причину для изменения.
// Плохо: UserManager обрабатывает и логику, и сохранение
class UserManager {
fun saveUser(user: User) { /*...*/ }
fun validateUser(user: User) { /*...*/ }
}
-
Open-Closed (OCP) — классы открыты для расширения, но закрыты для модификации. Используйте интерфейсы/абстракции.
-
Liskov Substitution (LSP) — подклассы должны заменять родительские классы без изменения поведения.
-
Interface Segregation (ISP) — клиенты не должны зависеть от интерфейсов, которые они не используют.
-
Dependency Inversion (DIP) — зависимости должны строиться на абстракциях, а не на конкретных реализациях.
Пример DIP:
interface Database {
fun save(data: String)
}
class UserRepository(private val db: Database) {
fun saveUser(user: User) = db.save(user.toString())
} Ответ 18+ 🔞
А, блядь, SOLID! Ну это ж классика, ёпта, как борщ со сметаной. Каждый второй джуниор на собеседовании про них слышал, но как применить — нихуя не понимает. Давай разжую, как для дебила, но с любовью.
Первый принцип — Single Responsibility (SRP). Это, блядь, основа основ. Класс должен делать одну хуйню и делать её хорошо. Не надо из него делать швейцарский нож, который и пользователя сохраняет, и валидирует, и письма шлёт, и чай заваривает. Представь себе чувака, который одновременно и сантехник, и электрик, и гинеколог. Доверия ебать ноль к такому специалисту. Вот и тут так же.
Смотри, как не надо делать, я прям чувствую подозрение ебать от такого кода:
// Плохо: UserManager — это пиздопроебибна на миллион строк
class UserManager {
fun saveUser(user: User) { /*...*/ } // Сохраняет в базу
fun validateUser(user: User) { /*...*/ } // Проверяет почту
fun sendWelcomeEmail(user: User) { /*...*/ } // Шлёт письмо
fun hashPassword(password: String) { /*...*/ } // Хеширует пароль
}
Этот класс — распиздяй конченый. Захотел поменять логику отправки писем — трогаешь класс, который отвечает за сохранение. В рот меня чих-пых! Всё развалится в один прекрасный день.
Второй — Open-Closed (OCP). Суть в том, что твой код должен быть как хороший диван: ты можешь накинуть на него новый чехольчик (расширить функционал), но сам диван перешивать не надо (не модифицировать существующий код). Достигается через интерфейсы и абстракции. Если ты каждый раз лезешь в работающую логику и что-то там меняешь — ты ходишь по охуенно тонкому льду, чувак.
Третий — Liskov Substitution (LSP). Звучит сложно, а на деле просто: если у тебя есть класс Утка и от него наследуется РезиноваяУтка, то везде, где в коде ожидается Утка, должна спокойно работать и РезиноваяУтка. Если твоя резиновая утка вдруг не крякает, а взрывается — это нарушение принципа, пидарас шерстяной. Подкласс не должен ломать ожидаемое поведение родителя.
Четвёртый — Interface Segregation (ISP). Не заставляй клиента реализовывать то, что ему не нужно. Это как если бы тебе для работы курьером выдали манда с ушами — полный комплект инструментов хирурга. Ты будешь таскать эту хуйню, а пользоваться только сумкой. Интерфейсы должны быть маленькими и целевыми.
Пятый — Dependency Inversion (DIP). Самый важный, пожалуй. Верхнеуровневые модули не должны зависеть от нижнеуровневых. И те, и другие должны зависеть от абстракций (интерфейсов). Проще говоря, не привязывайся к конкретной базе данных или внешнему сервису.
Вот, смотри, как это выглядит в коде. Вместо того чтобы врубать конкретную MySQL напрямую:
// Раньше было так: жёсткая привязка. Пиздец.
class UserRepository {
private val mySqlDatabase = MySQLDatabase() // Прикинь, завтра надо на PostgreSQL перейти!
fun saveUser(user: User) = mySqlDatabase.save(user.toString())
}
Делаем по-взрослому, с абстракцией:
// Абстракция. Это наш контракт.
interface Database {
fun save(data: String)
}
// Конкретная реализация. Может быть MySQL, PostgreSQL, хоть файлик на диске.
class MySQLDatabase : Database {
override fun save(data: String) { /* Реальная работа с MySQL */ }
}
// Класс-клиент. Ему похуй, что там внутри. Он знает только интерфейс.
class UserRepository(private val db: Database) { // Зависимость внедряется извне!
fun saveUser(user: User) = db.save(user.toString())
}
Ни хуя себе, правда? Теперь UserRepository не знает про MySQL. Он знает только, что у него есть некий Database, у которого есть метод save. Захотел поменять базу — создал новый класс PostgreSQLDatabase, реализующий тот же интерфейс, и подсунул его в репозиторий. Сам репозиторий не изменился ни на строчку. Это и есть гибкость.
Если эти принципы не соблюдать, через полгода твой проект превратится в монстра, которому пора вилкой в глаз или в жопу раз. А с SOLID есть шанс, что код будет жить долго и не заставит тебя каждую неделю ебеться-воробушки с бесконечными правками и багами.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶