SOLID
S — Single Responsibility — один класс/функция = одна ответственность
O — Open/Closed — открыт для расширения, закрыт для изменения
L — Liskov Substitution — подкласс можно подставить вместо родителя
I — Interface Segregation — лучше много мелких интерфейсов, чем один большой
D — Dependency Inversion — зависеть от абстракций, не от реализаций
S — Single Responsibility Principle
Принцип единственной ответственности
Смысл
Каждый модуль, класс или функция должна делать одно дело и иметь только одну причину для изменения.
Индикаторы нарушения
- Функция делает
fetch + transform + render- Компонент содержит логику вида «если сумма > 5000 — применить скидку» — это бизнес-решение, оно не должно жить в UI
// ❌ Нарушение — функция делает всё сразу
const processUser = (user) => {
validateUser(user);
saveToDatabase(user);
sendEmail(user);
};
// ✅ Каждая функция делает одно
const validate = (user) => { /* ... */ };
const save = (user) => { /* ... */ };
const notify = (user) => { /* ... */ };O — Open/Closed Principle
Принцип открытости/закрытости
Смысл
Модуль должен быть открыт для расширения, но закрыт для изменения. Новая фича — добавляем, не правим существующий код.
Индикаторы нарушения
- Каждый новый кейс требует
if/elseили правки существующего компонента
// ❌ Нарушение — добавление нового типа требует изменения функции
const getArea = (shape) => {
if (shape.type === 'circle') return Math.PI * shape.r ** 2;
if (shape.type === 'square') return shape.side ** 2;
// каждый новый тип → правка функции
};
// ✅ Расширяем через полиморфизм, не через if
class Circle { area() { return Math.PI * this.r ** 2; } }
class Square { area() { return this.side ** 2; } }
const getArea = (shape) => shape.area();L — Liskov Substitution Principle
Принцип подстановки Лисков
Смысл
Дочерний тип можно подставить вместо родительского, и программа не сломается. Дочерний обязан выполнять всё то, что обещает родительский — не урезая поведение и не нарушая его ожиданий.
Индикаторы нарушения
- Подкласс переопределяет метод так, что сужает контракт или бросает исключение там, где родитель работал (классический
Square extends Rectangle:setWidthломает ожидания дляRectangle)- Приходится проверять
instanceof ConcreteClassи ветвить логику вместо единообразной работы через интерфейс — значит, подтипы не взаимозаменяемы
I — Interface Segregation Principle
Принцип разделения интерфейсов
Смысл
Модуль не должен зависеть от того, что ему не нужно. Если интерфейс слишком большой — раздели его на меньшие, чтобы каждый модуль получал только то, с чем реально работает.
Индикаторы нарушения
- Класс вынужден реализовывать методы интерфейса, которые ему не нужны (пустые заглушки или
throw new Error('not supported'))- «Толстый» интерфейс: клиент зависит от объекта целиком, хотя использует 1–2 метода/поля — стоит разбить его на узкие интерфейсы
D — Dependency Inversion Principle
Принцип инверсии зависимостей
Смысл
Модули не должны напрямую зависеть друг от друга — между верхним и нижним уровнем встаёт абстракция (интерфейс или тип). Верхний уровень описывает что нужно через интерфейс, нижний реализует его — но они не знают друг о друге напрямую.
Индикаторы нарушения
- Компонент импортирует
fetchили работает с сырым API-ответом напрямую
// ❌ Зависим от конкретной реализации
class OrderService {
complete(order) {
new EmailSender().send(order.email, 'Done!'); // жёсткая зависимость
}
}
// ✅ Зависим от абстракции (любой mailer с методом send)
class OrderService {
constructor(mailer) { this.mailer = mailer; }
complete(order) { this.mailer.send(order.email, 'Done!'); }
}Шпаргалка по SOLID
| Принцип | Вопрос для проверки |
|---|---|
| S — Single Responsibility | Этот модуль делает только одно дело? |
| O — Open/Closed | Расширение добавляет новый код, не трогая существующий? |
| L — Liskov Substitution | Дочерний тип можно подставить вместо родительского без поломок? |
| I — Interface Segregation | Каждый модуль получает только то, с чем реально работает? |
| D — Dependency Inversion | Верхний уровень зависит от абстракции, а не от конкретной реализации? |
В React SOLID применяется на уровне компонентов: один компонент — одна ответственность, пропсы — интерфейс, кастомные хуки — инверсия зависимостей.
См. также
- gof — паттерны «Банды четырёх»
- dry-kiss-yagni
- lchc
- other-principles