🏗 SOLID - Пять заповедей программиста Почему один проект живет 10 лет и его легко дорабатывать, а другой через полгода превращается в "Legacy", к кото…
Почему один проект живет 10 лет и его легко дорабатывать, а другой через полгода превращается в "Legacy", к которому страшно подходить?
Разница в соблюдении принципов SOLID.
Это аббревиатура из 5 правил, сформулированных Робертом Мартином (Дядя Боб). Если вы нарушаете их - ваш код "гниет".
Давайте разберем каждую букву.
1️⃣ S - Single Responsibility Principle (Единственная ответственность)
"У класса должна быть только одна причина для изменения."
⛔ Как делают новички (God Object): Класс OrderService делает всё:
1. Считает сумму заказа.
2. Сохраняет заказ в БД.
3. Отправляет email клиенту.
4. Генерирует PDF-чек.
Если бизнес попросит изменить формат чека — мы лезем в этот класс. Если поменяется логика БД - опять в него. Риск сломать отправку писем при правке базы данных огромен!
✅ Как надо: Разбиваем на маленькие классы:
• OrderCalculator (считает).
• OrderRepository (сохраняет).
• EmailNotificationService (шлет письма).
• PdfGenerator (печатает).
OrderService теперь просто дирижер, который вызывает эти компоненты.
2️⃣ O - Open-Closed Principle (Открытость/Закрытость)
"Программные сущности должны быть открыты для расширения, но закрыты для модификации."
Это значит: Не меняйте старый рабочий код, чтобы добавить новую фичу.
⛔ Плохо: У нас есть метод расчета доставки.
if (deliveryType == "DHL") { ... }
else if (deliveryType == "Post") { ... }
// Пришла задача добавить FedEx? Придется лезть сюда и добавлять else if!
✅ Хорошо: Используем полиморфизм.
interface DeliveryStrategy { void deliver(); }
class DhlDelivery implements DeliveryStrategy { ... }
class PostDelivery implements DeliveryStrategy { ... }
// Нужен FedEx? Просто создаем НОВЫЙ класс, не трогая старые!
class FedExDelivery implements DeliveryStrategy { ... }
3️⃣ L - Liskov Substitution Principle (Принцип подстановки Барбары Лисков)
"Наследники должны без проблем заменять родителей."
Если у вас есть класс Bird с методом fly(), а вы создали наследника Penguin (Пингвин), который при вызове fly() бросает ошибку (потому что пингвины не летают) - вы нарушили LSP.
Суть: Если код работает с базовым классом, он должен работать и с любым его наследником, не зная об этом и не ломаясь.
4️⃣ I - Interface Segregation Principle (Разделение интерфейсов)
"Много маленьких интерфейсов лучше, чем один огромный."
⛔ Плохо: Интерфейс Worker имеет методы work() и eat().
Мы создаем класс Robot. Роботы работают, но не едят.
Нам придется реализовать метод eat() и оставить его пустым или кинуть ошибку. Это мусор.
✅ Хорошо: Разбейте на Workable и Eatable.
Человек имплементирует оба. Робот - только Workable.
5️⃣ D - Dependency Inversion Principle (Инверсия зависимостей)
"Зависьте от абстракций, а не от конкретики."
Это то, что мы учили в Spring (DI).
Ваш Service не должен зависеть от PostgresRepository. Он должен зависеть от интерфейса Repository.
Тогда вы сможете легко подменить Postgres на MySQL или Mock-объект для тестов, не меняя ни строчки в Сервисе.
SOLID - это фильтр. Прежде чем закоммитить код, спросите себя:
1. Не делает ли мой класс слишком много? (S)
2. Придется ли мне переписывать этот класс, если добавятся новые условия? (O)
3. Не ломаю ли я поведение родителя? (L)
4. Не заставляю ли я других реализовывать ненужные методы? (I)
5. Завишу ли я от интерфейсов или от конкретных классов? (D)
#Architecture #SOLID #CleanCode #OODesign
👉 @BookJava