Что такое 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 помогает организовать систему, которая легко развивается, тестируется, масштабируется и сопровождается.

**