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, Ассоциация агрегация композиция.