SOLID
SOLID — пять эвристик управления изменениями и зависимостями. Это не формальные законы и не цель «по одному интерфейсу на класс».
S — Single Responsibility
У модуля должна быть одна согласованная причина измениться. Парсинг файла, расчёт цены и отправка уведомления меняются по разным причинам — их не стоит запирать в одном классе.
O — Open/Closed
Стабильный код расширяется новой реализацией без правки каждой развилки.
interface FeeRule {
Money apply(Order order);
}
final class FeeCalculator {
private final List<FeeRule> rules;
Money calculate(Order order) {
return rules.stream()
.map(rule -> rule.apply(order))
.reduce(Money.ZERO, Money::add);
}
}Новая независимая FeeRule не требует switch по типам. Но не надо создавать extension point до появления реальной оси изменений.
L — Liskov Substitution
Подтип сохраняет контракт базового типа: не требует больше, не обещает меньше, сохраняет инварианты. UnsupportedOperationException в обычной операции интерфейса часто сигнализирует, что иерархия выбрана плохо.
I — Interface Segregation
Клиент зависит только от нужных операций. Несколько ролевых интерфейсов лучше одного «универсального», если потребители используют разные части.
D — Dependency Inversion
Высокоуровневая политика не зависит от инфраструктурной детали; обе стороны опираются на подходящую абстракцию. Абстракция принадлежит смыслу клиента, а не создаётся механически поверх каждого класса.
interface ExchangeRatePort {
Rate current(Currency from, Currency to);
}
final class PriceConversion {
private final ExchangeRatePort rates;
}Диагностика
- класс меняется из-за нескольких независимых команд — проверить SRP;
- новый вариант требует править много
if/switch— проверить OCP; - наследник запрещает базовые операции — проверить LSP;
- mock обязан реализовать десятки лишних методов — проверить ISP;
- бизнес-правило импортирует HTTP/JPA-класс — проверить DIP.
На собеседовании
SRP означает один метод в классе? Нет, одну согласованную причину изменения.
OCP запрещает менять существующий код? Нет; он предлагает стабилизировать уже понятную ось расширения.
DIP равен dependency injection? Нет. DI — способ передать зависимость, DIP — направление архитектурной зависимости.
Всегда ли больше интерфейсов лучше? Нет, абстракция должна выражать реальный контракт.
Связано: Инкапсуляция, Полиморфизм, Spring Dependency Injection и Inversion of Control, Ассоциация агрегация композиция.