SOLID — это аббревиатура пяти принципов объектно-ориентированного проектирования, введённых Робертом Мартином в начале 2000-х. Каждый принцип адресует конкретный сбой проектирования — симптомы, которые вы узнаёте в реальных кодовых базах: классы, которые невозможно расширить, модули, ломающиеся в неожиданных местах при изменении, код, который нельзя протестировать изолированно. Принципы SOLID — это не правила для механического применения, а линзы для выявления проблем проектирования и направления рефакторинга. В этом уроке каждый принцип разобран точно: сначала нарушение, затем исправление, затем архитектурные последствия.
Формулировка Мартина: «У класса должна быть только одна причина для изменения». Ключевое слово — «причина»: оно относится к актору или стейкхолдеру, чьи требования могут вызвать изменение, а не к синтаксическому правилу «один метод». Класс, который форматирует отчёты И сохраняет их в базу данных, имеет две причины для изменения: команда по отчётности (изменение формата) и команда эксплуатации (миграция базы данных). Когда эти изменения происходят одновременно, они конфликтуют. SRP говорит: эти задачи относятся к разным классам.
Формулировка Бертрана Мейера (1988), переосмысленная Мартином: «Программные сущности должны быть открыты для расширения, но закрыты для модификации». Класс закрыт для модификации, когда его проверенный, задеплоенный код не нужно менять для добавления нового поведения. Он открыт для расширения, когда новое поведение добавляется написанием нового кода — нового подкласса, новой реализации стратегии, нового декоратора — без прикосновения к существующему классу. Самый распространённый механизм: зависеть от абстракций (интерфейсов) и внедрять конкретные реализации.
Формулировка Барбары Лисков (1987): «Если S является подтипом T, то объекты типа T можно заменять объектами типа S, не нарушая желательных свойств программы». Простыми словами: подкласс должен соблюдать контракт суперкласса — не только сигнатуры методов, но и поведенческие ожидания (предусловия, постусловия, инварианты). Классическое нарушение LSP — проблема Квадрата-Прямоугольника: Square расширяет Rectangle, но переопределение setWidth с одновременным изменением height нарушает контракт прямоугольника о независимости ширины и высоты.
Формулировка Мартина: «Клиенты не должны зависеть от интерфейсов, которые они не используют». Толстые интерфейсы — с множеством несвязанных методов — вынуждают реализаторов создавать заглушки для методов, которые им не нужны, а клиентов — импортировать тип, несущий возможности, которые им не нужны. ISP говорит: предпочитайте множество маленьких клиентских интерфейсов одному большому. Проектное давление ISP естественно порождает Ролевые Интерфейсы — интерфейсы, описывающие роль объекта (Printable, Saveable, Auditable), а не полный перечень всего, что объект умеет.
Формулировка Мартина: «(A) Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. (B) Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций». DIP — это принцип, лежащий в основе Луковой архитектуры, Гексагональной архитектуры и Чистой архитектуры. Он же делает внедрение зависимостей осмысленным: DI — это механизм; DIP — это причина.