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

Ответ

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

Ответ 18+ 🔞

Давай разжую тебе эти ваши SOLID-принципы, а то читаешь как мантру, а нихуя не въезжаешь, что за магия. Представь, что это не правила, а пять заповедей для того, чтобы твой код не превратился в говно, в которое страшно заглянуть через месяц.

1. Принцип единственной ответственности (Single Responsibility Principle - SRP) Это про то, чтобы не делать из класса швейцарского ножа, который и пашет, и сеет, и в жопу дует. Одна штука — одна работа. Всё.

// Вот смотри, сраный бардак. Класс User — он про пользователя или про базу данных?
class User {
    private String name;
    public void saveToDatabase() { /* ... */ } // Это уже не его ебаная работа!
}
// А вот так — по кайфу. Каждый занимается своим делом.
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) Не надо делать один интерфейс-монстра на все случаи жизни. Это как заставить тостер реализовывать метод стиратьБелье(). Сделай несколько маленьких и удобных.

// Вот смотри на этот ужас. Зачем принтеру сканировать?
interface MultiFunctionDevice { void print(); void scan(); } // ISP в жопу
// А вот так — элегантно. Хочешь печатать — бери Printer. Хочешь сканировать — бери Scanner.
interface Printer { void print(); }
interface Scanner { void scan(); }

5. Принцип инверсии зависимостей (Dependency Inversion Principle - DIP) Высокоуровневые модули (логика приложения) не должны зависеть от низкоуровневых (работа с базой, файлами). Оба должны зависеть от абстракций. Это чтобы можно было, например, сменить базу данных с MySQL на PostgreSQL, не переписывая половину бизнес-логики.

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

Если вкратце, то SOLID — это не какая-то академическая хуйня, а реальные способы не выстрелить себе в колено, когда проект вырастет больше, чем "Hello, World!". Следуешь им — код легче тестировать, менять и понимать. Не следуешь — получаешь спагетти-код, который через полгода сам же и проклянёшь.