Кто и когда впервые сформулировал принципы SOLID?

Ответ

Автор: Роберт Мартин (Robert C. Martin) Год: 2000 Документ: Статья "Design Principles and Design Patterns" Популяризация: Майкл Фэзерс (Michael Feathers) ввёл акроним SOLID

Расшифровка принципов:

  1. Single Responsibility — единственная ответственность
  2. Open-Closed — открытость/закрытость
  3. Liskov Substitution — подстановка Лисков
  4. Interface Segregation — разделение интерфейсов
  5. Dependency Inversion — инверсия зависимостей

Пример нарушения и соблюдения SRP в Swift:

// ❌ Нарушение: класс управляет и данными, и уведомлениями
class UserManager {
    func saveUser(_ user: User) { /* Сохранение в БД */ }
    func sendWelcomeEmail(to user: User) { /* Отправка email */ }
}

// ✅ Соблюдение: разделение ответственности
class UserRepository {
    func save(_ user: User) { /* Только сохранение */ }
}

class EmailService {
    func sendWelcome(to user: User) { /* Только отправка */ }
}

Практическая польза: SOLID повышает поддерживаемость, тестируемость и гибкость кода.

Ответ 18+ 🔞

Да ты послушай, что эти умники напридумывали! Сидят, блядь, в двухтысячном году, Роберт Мартин, значит, такой, выкатывает статью про принципы дизайна. Ну, типа, как код писать, чтобы он не превращался в такую кашу, что потом сам чёрт ногу сломит, в рот меня чих-пых!

А потом подъехал Майкл Фэзерс, хитрая жопа, и говорит: «А давайте я вам из этого букет принципов акроним слеплю, SOLID, чтобы запоминалось легко!». Ну и слепил, ёпта. И понеслась.

Итак, SOLID, блядь, это не про твёрдое состояние, а про то, как твой код должен быть не как сопля, а чтоб его можно было менять, не разнеся половину проекта к ебеням.

S — Single Responsibility. Единственная ответственность, сука. Это как если у тебя есть молоток, то он должен забивать гвозди, а не ещё и шурупы крутить, и суп варить. Класс должен делать что-то одно, пиздец как просто. А то начнёшь ты в одном классе и пользователей сохранять, и им письма слать, и отчёты генерить — это ж манда с ушами получится! Однажды что-то сломается в отправке писем, а у тебя заодно и сохранение в базу встанет колом. Удивление пиздец!

Смотри, как бывает:

// ❌ Нарушение: класс-универсал, он же швейцарский нож, он же говно
class UserManager {
    func saveUser(_ user: User) { /* Сохранение в БД */ }
    func sendWelcomeEmail(to user: User) { /* Отправка email */ }
}

Вот этот UserManager — он уже мудак. Он и в базу пихает, и по почте шлёт. А если почтовый сервер ляжет? Всё, сохранение пользователя тоже накроется медным тазом. Терпения ноль ебать!

А теперь по-человечески:

// ✅ Соблюдение: каждый сверчок знай свой шесток
class UserRepository {
    func save(_ user: User) { /* Только сохранение. Тише, блядь, мыши, Бог услышит! */ }
}

class EmailService {
    func sendWelcome(to user: User) { /* Только отправка. И ни хуя больше! */ }
}

Вот! UserRepository — тупо таскает данные туда-сюда. EmailService — только письма шмаляет. Один сломался — второй живёт и работает. Красота, бля!

O — Open-Closed. Суть в том, что класс должен быть открыт для расширения, но закрыт для изменений. То есть ты не лезешь в его старый, отлаженный код с криками «щас я тут кое-что подпилю!», а навешиваешь новую функциональность сверху, через наследование или композицию. А то начнёшь пилить — и оп, волнение ебать, ты уже пол-системы разъебал.

L — Liskov Substitution. Подстановка Лисков, блядь. Звучит как заклинание, а смысл простой: если у тебя есть класс Утка, и от него наследуется РезиноваяУтка, то везде, где работает Утка, должна спокойно работать и РезиноваяУтка. Если твоя резиновая утка вместо «кря-кря» начинает стрелять ракетами или, того хуже, тонуть — это пиздец, принцип нарушен. Наследник не должен ломать логику предка, иначе это не наследник, а пидарас шерстяной.

I — Interface Segregation. Разделение интерфейсов. Не надо делать один здоровенный, жирный интерфейс на все случаи жизни. Это как если бы ты пришёл в ресторан, а тебе дали меню на 500 страниц, где есть и «бифштекс», и «починить кран», и «сделать укол». Сделай несколько маленьких и точных. Класс должен реализовывать только то, что ему реально нужно, а не тащить на себе кучу ненужных методов, которые он никогда не вызовет.

D — Dependency Inversion. Инверсия зависимостей. Высокоуровневые модули не должны зависеть от низкоуровневых. Оба должны зависеть от абстракций. Проще говоря, не пиши class Engine { let carburetor = OldShittyCarburetor() }. Пиши class Engine { let fuelSystem: FuelSystem }. И тогда ты сможешь подать в движок и карбюратор, и инжектор, и хоть ведро с бензином. Гибкость — овердохуища!

А в чём, собственно, соль? А соль в том, что если этим принципам следовать, то код становится не монолитной глыбой, которую страшно тронуть, а как конструктор. Захотел поменять отправку писем с email на SMS — хук-плюх, заменил один сервис другим, и ничего больше не полетело. Тестировать такое — одно удовольствие, а не ебаться с моками полжизни. Поддерживать — тоже, потому что всё разложено по полочкам, а не свалено в одну кучу, как в комнате у подростка.

В общем, SOLID — это не догма, а, блядь, очень дельные советы от бывалых, как не выстрелить себе в ногу на ровном месте. Потому что иначе потом сидишь и думаешь: «Ну я же всего одну строчку добавил… а почему всё горит?». А горит оно потому, что ты, чувак, ходишь по охуенно тонкому льду своего же кривого кода.