Ответ
SOLID — это набор из пяти принципов, которые делают дизайн программ гибким, понятным и поддерживаемым.
1. SRP (Single Responsibility Principle) — Принцип единственной ответственности:
Класс должен иметь одну и только одну причину для изменения.
Нарушение:
class Report {
void generateContent() { /* ... */ }
void formatToPDF() { /* ... */ } // Ответственность за форматирование
void saveToFile(String path) { /* ... */ } // Ответственность за ввод-вывод
}
Следование:
class Report {
void generateContent() { /* ... */ }
}
class ReportPDFFormatter {
void format(Report report) { /* ... */ }
}
class ReportFileSaver {
void save(Report report, String path) { /* ... */ }
}
2. OCP (Open/Closed Principle) — Принцип открытости/закрытости:
Программные сущности должны быть открыты для расширения, но закрыты для модификации.
Достигается через абстракции и полиморфизм:
interface DiscountStrategy {
double apply(double price);
}
class RegularDiscount implements DiscountStrategy { /* ... */ }
class PremiumDiscount implements DiscountStrategy { /* ... */ }
class OrderProcessor {
private DiscountStrategy discount;
// Можно добавить новый тип скидки, НЕ изменяя OrderProcessor
public OrderProcessor(DiscountStrategy discount) { this.discount = discount; }
public double calculateTotal(double price) { return discount.apply(price); }
}
3. LSP (Liskov Substitution Principle) — Принцип подстановки Барбары Лисков:
Объекты базового класса должны быть заменяемы объектами производных классов без изменения корректности программы.
Нарушение:
class Rectangle {
protected int width, height;
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
}
class Square extends Rectangle {
@Override
void setWidth(int w) { width = height = w; } // Меняет и высоту!
@Override
void setHeight(int h) { width = height = h; } // Меняет и ширину!
}
// Клиентский код ломается, ожидая независимого изменения сторон
void testArea(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert r.width * r.height == 20; // Упадет для Square
}
4. ISP (Interface Segregation Principle) — Принцип разделения интерфейсов:
Много специализированных интерфейсов лучше, чем один универсальный.
Плохо:
interface Worker {
void work();
void eat();
void code(); // Не всем нужен
}
class Robot implements Worker {
void work() { /* ... */ }
void eat() { /* НЕ НУЖНО! */ } // Нарушение ISP
void code() { /* ... */ }
}
Хорошо:
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Codable { void code(); }
class Human implements Workable, Eatable, Codable { /* ... */ }
class Robot implements Workable, Codable { /* ... */ } // Только нужные интерфейсы
5. DIP (Dependency Inversion Principle) — Принцип инверсии зависимостей:
Зависимости должны строиться на абстракциях, а не на деталях. Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
Достигается через Dependency Injection (DI):
// Уровень бизнес-логики (высокий) зависит от абстракции
interface Repository {
void save(Data data);
}
class Service {
private final Repository repository; // Зависимость от интерфейса
public Service(Repository repository) { // Внедрение через конструктор
this.repository = repository;
}
public void process(Data data) {
// ... логика
repository.save(data); // Не знает о конкретной реализации
}
}
// Уровень доступа к данным (низкий) реализует абстракцию
class PostgresRepository implements Repository { /* ... */ }
class MongoRepository implements Repository { /* ... */ }
Итог: Применение SOLID ведет к созданию слабосвязанных (loosely coupled), тестируемых и легко расширяемых систем.