Каковы принципы SOLID в объектно-ориентированном проектировании?

«Каковы принципы SOLID в объектно-ориентированном проектировании?» — вопрос из категории Основы программирования, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

SOLID — это акроним пяти основных принципов проектирования, цель которых — создание понятного, гибкого и поддерживаемого объектно-ориентированного кода.

1. Принцип единственной ответственности (Single Responsibility Principle - SRP)

Класс должен иметь одну и только одну причину для изменения (одну ответственность).

// Нарушение SRP: класс занимается и логикой пользователя, и его сохранением.
class User {
private String name;
public void saveToDatabase() { /* ... */ } // Ответственность №2
}
// Соблюдение SRP:
class User { /* Только данные и бизнес-логика */ }
class UserRepository { /* Только сохранение/загрузка из БД */ }

2. Принцип открытости/закрытости (Open/Closed Principle - OCP)

Классы должны быть открыты для расширения, но закрыты для модификации.

// Новый тип фигуры добавляется без изменения существующего кода.
interface Shape { double area(); }
class Circle implements Shape { /* ... */ }
class Square implements Shape { /* ... */ }
class AreaCalculator {
public double totalArea(List<Shape> shapes) { /* ... */ } // Закрыт для изменений
}

3. Принцип подстановки Барбары Лисков (Liskov Substitution Principle - LSP)

Объекты базового класса должны быть заменяемы объектами производных классов без изменения корректности программы.

4. Принцип разделения интерфейса (Interface Segregation Principle - ISP)

Много специализированных интерфейсов лучше, чем один универсальный.

// Плохо: Printer вынужден реализовывать ненужный метод scan().
interface MultiFunctionDevice { void print(); void scan(); }
// Хорошо:
interface Printer { void print(); }
interface Scanner { void scan(); }

5. Принцип инверсии зависимостей (Dependency Inversion Principle - DIP)

Зависимости должны строиться на абстракциях, а не на деталях. Модули верхнего уровня не должны зависеть от модулей нижнего уровня.

// Зависимость от абстракции (интерфейса), а не от конкретной БД.
interface Database { void save(Object data); }
class MySQLDatabase implements Database { /* ... */ }
class UserService {
private final Database db; // Абстракция
public UserService(Database db) { this.db = db; } // Внедрение зависимости
}

Следование SOLID облегчает тестирование, рефакторинг и снижает связность кода.