SOLID
SOLID — акроним из пяти принципов объектно-ориентированного проектирования: SRP, OCP, LSP, ISP и DIP, помогающих держать код пригодным к изменениям.
Что такое SOLID
SOLID (по-русски часто пишут «солид») — акроним из пяти принципов объектно-ориентированного проектирования. Принципы сформулировал Роберт Мартин (Uncle Bob) на рубеже 2000-х, а в запоминающийся акроним их сложил Майкл Фэзерс.
Задача у всех пяти одна: сделать код пригодным к изменениям. Не «правильным», не «красивым» — именно дешёвым в правке, когда через год придёт новое требование.
SOLID: расшифровка
| Буква | Принцип | По-русски | Суть в одной строке |
|---|---|---|---|
| S | Single Responsibility Principle | Единственная ответственность | У класса одна причина для изменения |
| O | Open/Closed Principle | Открытость/закрытость | Открыт для расширения, закрыт для изменения |
| L | Liskov Substitution Principle | Подстановка Лисков | Наследник должен подходить вместо родителя |
| I | Interface Segregation Principle | Разделение интерфейсов | Много узких интерфейсов лучше одного широкого |
| D | Dependency Inversion Principle | Инверсия зависимостей | Зависеть от абстракций, а не от реализаций |
S — Single Responsibility
Классическая формулировка «класс делает одну вещь» неточна. Точнее: у класса должна быть одна причина для изменения — он отвечает перед одной группой заказчиков.
# нарушение: три причины меняться — правила отчёта, формат вывода, способ доставки
class Report:
def calculate(self) -> dict: ...
def render_html(self) -> str: ...
def send_email(self) -> None: ...
Правки бухгалтерии, вёрстки и почтовиков сталкиваются в одном файле. Разделите на ReportCalculator, HtmlRenderer, EmailSender — каждый меняется независимо.
O — Open/Closed
Новое поведение добавляется новым кодом, а не правкой работающего. Признак нарушения — растущий if/elif по типу:
# нарушение: каждый новый формат — правка функции
def export(data, fmt):
if fmt == "csv": ...
elif fmt == "json": ...
elif fmt == "xlsx": ... # и так до бесконечности
Лечится паттерном Strategy: интерфейс Exporter и по классу на формат. Подробнее — в паттернах проектирования.
L — Liskov Substitution
Если код работает с базовым типом, он обязан работать и с любым наследником — без проверок isinstance и сюрпризов. Хрестоматийный контрпример — Square extends Rectangle: у прямоугольника ширина и высота независимы, у квадрата нет, и код вида «поставь ширину 5, высоту 4, ожидай площадь 20» ломается.
Практические признаки нарушения: наследник бросает исключение там, где родитель работал; ужесточает предусловия; возвращает None вместо списка.
I — Interface Segregation
Клиента нельзя заставлять зависеть от методов, которыми он не пользуется. Интерфейс Worker с методами work(), eat() и sleep() заставит класс робота реализовывать eat() заглушкой. Разбейте на Workable, Feedable, Sleepable.
D — Dependency Inversion
Модули верхнего уровня не должны зависеть от нижнего — оба зависят от абстракций.
# нарушение: сервис намертво привязан к конкретной СУБД
class UserService:
def __init__(self) -> None:
self.db = PostgresClient(host="10.0.0.5") # попробуйте это протестировать
# инверсия: зависимость приходит снаружи
class UserService:
def __init__(self, repo: UserRepository) -> None:
self.repo = repo
Второй вариант тестируется с фейковым репозиторием в памяти, переезжает с PostgreSQL на другое хранилище без правки бизнес-логики и не требует живой базы в юнит-тестах. Это самый практически ценный принцип из пяти.
Критика и здравый смысл
SOLID — эвристики, а не законы. Реальные претензии к ним стоит знать:
- Формулировки размыты. «Одна ответственность» — понятие резиновое, два инженера проведут границу по-разному.
- Легко переусердствовать. Буквальное следование SOLID рождает пятнадцать интерфейсов с одной реализацией каждый. Абстракция ради абстракции — это стоимость без выгоды.
- Принципы про ООП. В функциональном коде часть из них тривиальна или бессмысленна: OCP решается передачей функции аргументом.
Разумное применение: заметили, что класс тяжело менять или невозможно протестировать, — ищите, какой принцип нарушен. Не наоборот.
Связанные термины
- Паттерны проектирования — конкретные приёмы, реализующие принципы SOLID
- Концепции разработки — DRY, KISS, YAGNI и остальная база
- Java — язык, на котором SOLID обычно и объясняют
- Python — где часть принципов закрывается средствами языка
- Code review — где о нарушениях SOLID и спорят
Готовы запустить GPU-задачу?
Запустить GPU-сервер