O, L, I, D
Четыре оставшихся принципа SOLID (первый, SRP, — в теме «S — единственная ответственность»):
O — Open/Closed. Класс должен быть открыт для расширения, закрыт для изменения — добавляешь новое поведение через новый код, не трогая старый:
// вместо if-else по типу — новый класс на каждый случай (паттерн Strategy)
interface Discount { int apply(int price); }
class NewYearDiscount implements Discount { ... } // добавил — старое не тронул
L — Liskov Substitution. Наследник должен полностью заменять родителя без сюрпризов — подробный разбор с примером “квадрат-прямоугольник” в теме «LSP, композиция vs наследование».
I — Interface Segregation. Лучше много маленьких интерфейсов, чем один большой “интерфейс на всё”:
// ❌ плохо: реализующий класс вынужден писать пустые методы
interface Worker { void work(); void eat(); void sleep(); }
// ✅ хорошо: реализуешь только то, что реально нужно
interface Workable { void work(); }
interface Eatable { void eat(); }
class Robot implements Workable { void work() { ... } } // роботу не нужен eat()
D — Dependency Inversion. Зависеть от абстракций (интерфейсов), а не от конкретных реализаций:
// ❌ плохо: жёстко привязан к конкретному классу
class OrderService {
private MySqlRepository repo = new MySqlRepository();
}
// ✅ хорошо: зависит от интерфейса, реализацию можно подменить
class OrderService {
private final OrderRepository repo; // интерфейс
OrderService(OrderRepository repo) { this.repo = repo; } // подставляется снаружи
}
Копнуть глубже
Open/Closed на практике почти всегда реализуется через полиморфизм/паттерны (Strategy, Decorator) — вместо того чтобы лезть в существующий метод и дописывать туда ещё один else if, создаёшь новый класс, реализующий тот же интерфейс.
Interface Segregation предотвращает “толстые” интерфейсы, которые заставляют классы реализовывать ненужные им методы пустышками — это явный признак, что интерфейс делает слишком много и его пора разделить.
Dependency Inversion — основа всего Spring. Когда внедряешь зависимость через конструктор (@Autowired или просто конструктор с интерфейсом параметром), ты явно следуешь этому принципу: OrderService не создаёт OrderRepository сам, а получает его готовым снаружи — это и называется инверсией управления (IoC), на которой держится весь Spring.
• как Dependency Inversion связан с тем, как работает Spring (если дошёл до 2-го слоя).