Что такое Solid
SOLID — это аббревиатура из пяти принципов объектно-ориентированного программирования, призванных сделать архитектуру программного обеспечения более читаемой, гибкой и легко масштабируемой. Эти принципы были популяризированы Робертом Мартином (Robert C. Martin), известным как Uncle Bob. Они широко применяются при проектировании классов, интерфейсов, модулей и систем в целом. Несмотря на своё происхождение в объектно-ориентированной парадигме, эти принципы полезны и в функциональном, и в процедурном программировании при адаптации под соответствующие паттерны.
S — Single Responsibility Principle (Принцип единственной ответственности)
Суть:
У класса должна быть только одна причина для изменения, то есть он должен выполнять только одну задачу.
Обоснование:
Когда класс выполняет несколько обязанностей (например, сохраняет данные в базу и форматирует их для отчёта), то изменение одной обязанности может повлиять на другие.
Пример:
Плохо:
class Report {
void generate() { ... }
void print() { ... }
void saveToFile() { ... }
}
Хорошо:
class ReportGenerator {
void generate() { ... }
}
class ReportPrinter {
void print(Report report) { ... }
}
class ReportSaver {
void saveToFile(Report report) { ... }
}
Каждый класс отвечает только за одну задачу.
O — Open/Closed Principle (Принцип открытости/закрытости)
Суть:
Классы должны быть открыты для расширения, но закрыты для модификации.
Обоснование:
Изменения существующего кода несут риск нарушения логики. Лучше расширять поведение, не изменяя уже работающий код.
Способ реализации:
-
Наследование
-
Интерфейсы
-
Абстракции
-
Декораторы
Пример:
interface Discount {
double apply(double price);
}
class NoDiscount implements Discount {
public double apply(double price) {
return price;
}
}
class SeasonalDiscount implements Discount {
public double apply(double price) {
return price \* 0.9;
}
}
class Product {
double getPrice(Discount discount) {
return discount.apply(basePrice);
}
}
Добавление новых скидок не требует изменения кода Product.
L — Liskov Substitution Principle (Принцип подстановки Барбары Лисков)
Суть:
Объекты подкласса должны быть взаимозаменяемы с объектами своего базового класса без нарушения поведения программы.
Обоснование:
Наследование должно использоваться таким образом, чтобы клиентский код не нуждался в изменениях, если в него передаётся экземпляр подкласса.
Нарушение:
class Bird {
void fly() { ... }
}
class Ostrich extends Bird {
void fly() {
throw new UnsupportedOperationException();
}
}
Остраус не умеет летать, но Bird ожидается как летающий. Такой дизайн нарушает принцип Лисков. Следует использовать композицию или пересмотреть иерархию.
I — Interface Segregation Principle (Принцип разделения интерфейсов)
Суть:
Клиенты не должны зависеть от интерфейсов, которые они не используют.
Обоснование:
Лучше иметь несколько узкоспециализированных интерфейсов, чем один большой.
Пример:
Плохо:
interface Machine {
void print();
void scan();
void fax();
}
Класс OldPrinter не умеет сканировать и факсить, но должен реализовать эти методы.
Хорошо:
interface Printer {
void print();
}
interface Scanner {
void scan();
}
class OldPrinter implements Printer {
void print() { ... }
}
Теперь классы реализуют только те интерфейсы, которые им нужны.
D — Dependency Inversion Principle (Принцип инверсии зависимостей)
Суть:
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали зависят от абстракций.
Обоснование:
Это обеспечивает слабую связанность и даёт гибкость в конфигурации зависимостей.
Реализация:
-
Использование интерфейсов или абстрактных классов.
-
Внедрение зависимостей (Dependency Injection).
Пример:
interface Database {
void save(String data);
}
class MySQLDatabase implements Database {
public void save(String data) { ... }
}
class ReportService {
private final Database db;
public ReportService(Database db) {
this.db = db;
}
void generateReport() {
db.save("data");
}
}
Класс ReportService зависит от абстракции Database, а не от конкретной реализации MySQLDatabase. Это позволяет легко подменять реализацию.
Применение SOLID на практике
Преимущества:
-
Повышает поддерживаемость кода;
-
Облегчает тестирование (особенно модульное);
-
Уменьшает связность между компонентами;
-
Облегчает расширение функциональности без риска поломки старой логики;
-
Помогает разрабатывать архитектуру, устойчивую к изменениям.
Примеры применения:
-
Backend-приложения (Java, C#, Go — с адаптацией);
-
Разработка REST API с раздельной логикой контроллеров, сервисов и репозиториев;
-
iOS и Android приложения (через MVVM, VIPER);
-
Фронтенд-приложения (через SOLID-подход в компонентах, сервисах, хранилищах состояния и архитектуре).
SOLID и другие архитектурные принципы
SOLID тесно связан с:
-
DRY (Don't Repeat Yourself) — избегание дублирования логики;
-
KISS (Keep It Simple, Stupid) — простота решений;
-
YAGNI (You Ain’t Gonna Need It) — не реализовывай, пока не потребуется;
-
Composition over Inheritance — предпочтение композиции над наследованием;
-
Clean Architecture — архитектура с явным разделением слоёв и зависимостей.
SOLID помогает организовать систему, которая легко развивается, тестируется, масштабируется и сопровождается.
**