SOLID — это набор из ......... пяти принципов объектно‑ориентированного проектирования, повышающих гибкость и поддерживаемость кода. Ниже кратко о каждом принципе с конкретными примерами (нарушение → исправление) в контексте небольшого Java‑проекта управления заказами. 1) SRP — Single Responsibility Principle (принцип единственной ответственности) - Суть: класс должен иметь одну причину для изменения — одну ответственность. - Нарушение (плохой код): ```java class OrderService { void placeOrder(Order o) { double total = calculateTotal(o); saveToDb(o); sendConfirmationEmail(o); logToFile(o); } // расчёт, сохранение, отправка писем, логирование — всё в одном классе } ``` - Исправление (разбиение обязанностей): ```java class OrderService { private final OrderRepository repo; private final NotificationService notifier; private final DiscountCalculator discount; void placeOrder(Order o) { o.setTotal(discount.apply(o)); repo.save(o); notifier.sendConfirmation(o); } } ``` Разделили: OrderRepository, NotificationService, DiscountCalculator — каждая зона ответственности в отдельном классе. 2) OCP — Open/Closed Principle (открыт для расширения, закрыт для модификации) - Суть: поведение системы можно расширять без изменения существующего кода. - Нарушение: ```java class DiscountCalculator { double apply(Order o) { if (o.type == OrderType.SEASON) return o.total * 0.9; if (o.type == OrderType.VIP) return o.total * 0.8; // при добавлении нового типа — меняем этот класс } } ``` - Исправление (использовать полиморфизм/стратегии): ```java interface DiscountRule { double apply(Order o); } class VipDiscount implements DiscountRule { public double apply(Order o) { return o.getTotal()*0.8; } } class SeasonDiscount implements DiscountRule { public double apply(Order o) { return o.getTotal()*0.9; } } class DiscountCalculator { private final List rules; double apply(Order o) { return rules.stream().reduce(o.getTotal(), (t, r) -> r.apply(o), (a,b)->b); } } ``` Новые правила добавляются как новые классы, не трогая существующий код. 3) LSP — Liskov Substitution Principle (принцип подстановки Барбары Лисков) - Суть: объекты подклассов должны заменять объекты базового класса без нарушения ожидаемого поведения. - Нарушение: ```java class Order { void cancel() { /* отмена */ } } class PaidOrder extends Order { @Override void cancel() { throw new UnsupportedOperationException("Paid orders can't be canceled"); } } // Код, который вызывает cancel() на Order, ломается для PaidOrder ``` - Исправление (разделить интерфейсы/модели): ```java interface Cancellable { void cancel(); } class Order { /* общая логика */ } class CancellableOrder extends Order implements Cancellable { public void cancel() { /* ... */ } } class PaidOrder extends Order { /* не реализует Cancellable */ } ``` Клиент коду вызывает cancel() только для объектов, реализующих Cancellable, LSP сохраняется. 4) ISP — Interface Segregation Principle (принцип разделения интерфейсов) - Суть: клиенты не должны зависеть от методов, которыми они не пользуются. Интерфейсы — узкие, ориентированные. - Нарушение: ```java interface OrderOperations { void ship(Order o); void refund(Order o); void printInvoice(Order o); } class WarehouseService implements OrderOperations { public void ship(Order o) { /* ok */ } public void refund(Order o) { throw new UnsupportedOperationException(); } // не нужно, но реализовать надо public void printInvoice(Order o) { throw new UnsupportedOperationException(); } } ``` - Исправление (разделить интерфейсы): ```java interface Shippable { void ship(Order o); } interface Refundable { void refund(Order o); } interface InvoicePrintable { void printInvoice(Order o); } class WarehouseService implements Shippable { public void ship(Order o) { /* ... */ } } ``` Каждый сервис реализует только необходимые методы. 5) DIP — Dependency Inversion Principle (принцип инверсии зависимостей) - Суть: высокоуровневые модули не должны зависеть от низкоуровневых; оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей; детали — от абстракций. - Нарушение: ```java class OrderService { private final EmailNotifier notifier = new EmailNotifier(); // жёсткая зависимость void placeOrder(Order o) { notifier.send(o); } } ``` - Исправление (зависимость от интерфейса, DI): ```java interface Notifier { void send(Order o); } class EmailNotifier implements Notifier { public void send(Order o){ /* ... */ } } class OrderService { private final Notifier notifier; OrderService(Notifier notifier) { this.notifier = notifier; } // внедряем зависимость void placeOrder(Order o) { notifier.send(o); } } ``` Теперь можно подменять реализацию (Email, SMS, Mock) без изменения OrderService. Короткие рекомендации при проектировании order‑системы: - Делите обязанности (SRP): отдельные классы для хранения, оплаты, уведомлений, расчёта скидок. - Расширяйте через интерфейсы/стратегии (OCP). - Разделяйте интерфейсы по ролям (ISP). - Не привязывайтесь к конкретным реализациям; используйте DI/контейнеры (DIP). - Избегайте подклассов, которые нарушают контракт базового класса; при сомнении выделяйте дополнительные интерфейсы (LSP). Если нужно, могу привести более развёрнутые Java‑файлы (полные классы) для каждой иллюстрации.
1) SRP — Single Responsibility Principle (принцип единственной ответственности)
- Суть: класс должен иметь одну причину для изменения — одну ответственность.
- Нарушение (плохой код):
```java
class OrderService {
void placeOrder(Order o) {
double total = calculateTotal(o);
saveToDb(o);
sendConfirmationEmail(o);
logToFile(o);
}
// расчёт, сохранение, отправка писем, логирование — всё в одном классе
}
```
- Исправление (разбиение обязанностей):
```java
class OrderService {
private final OrderRepository repo;
private final NotificationService notifier;
private final DiscountCalculator discount;
void placeOrder(Order o) {
o.setTotal(discount.apply(o));
repo.save(o);
notifier.sendConfirmation(o);
}
}
```
Разделили: OrderRepository, NotificationService, DiscountCalculator — каждая зона ответственности в отдельном классе.
2) OCP — Open/Closed Principle (открыт для расширения, закрыт для модификации)
- Суть: поведение системы можно расширять без изменения существующего кода.
- Нарушение:
```java
class DiscountCalculator {
double apply(Order o) {
if (o.type == OrderType.SEASON) return o.total * 0.9;
if (o.type == OrderType.VIP) return o.total * 0.8;
// при добавлении нового типа — меняем этот класс
}
}
```
- Исправление (использовать полиморфизм/стратегии):
```java
interface DiscountRule { double apply(Order o); }
class VipDiscount implements DiscountRule { public double apply(Order o) { return o.getTotal()*0.8; } }
class SeasonDiscount implements DiscountRule { public double apply(Order o) { return o.getTotal()*0.9; } }
class DiscountCalculator {
private final List rules;
double apply(Order o) {
return rules.stream().reduce(o.getTotal(), (t, r) -> r.apply(o), (a,b)->b);
}
}
```
Новые правила добавляются как новые классы, не трогая существующий код.
3) LSP — Liskov Substitution Principle (принцип подстановки Барбары Лисков)
- Суть: объекты подклассов должны заменять объекты базового класса без нарушения ожидаемого поведения.
- Нарушение:
```java
class Order {
void cancel() { /* отмена */ }
}
class PaidOrder extends Order {
@Override
void cancel() { throw new UnsupportedOperationException("Paid orders can't be canceled"); }
}
// Код, который вызывает cancel() на Order, ломается для PaidOrder
```
- Исправление (разделить интерфейсы/модели):
```java
interface Cancellable { void cancel(); }
class Order { /* общая логика */ }
class CancellableOrder extends Order implements Cancellable { public void cancel() { /* ... */ } }
class PaidOrder extends Order { /* не реализует Cancellable */ }
```
Клиент коду вызывает cancel() только для объектов, реализующих Cancellable, LSP сохраняется.
4) ISP — Interface Segregation Principle (принцип разделения интерфейсов)
- Суть: клиенты не должны зависеть от методов, которыми они не пользуются. Интерфейсы — узкие, ориентированные.
- Нарушение:
```java
interface OrderOperations {
void ship(Order o);
void refund(Order o);
void printInvoice(Order o);
}
class WarehouseService implements OrderOperations {
public void ship(Order o) { /* ok */ }
public void refund(Order o) { throw new UnsupportedOperationException(); } // не нужно, но реализовать надо
public void printInvoice(Order o) { throw new UnsupportedOperationException(); }
}
```
- Исправление (разделить интерфейсы):
```java
interface Shippable { void ship(Order o); }
interface Refundable { void refund(Order o); }
interface InvoicePrintable { void printInvoice(Order o); }
class WarehouseService implements Shippable { public void ship(Order o) { /* ... */ } }
```
Каждый сервис реализует только необходимые методы.
5) DIP — Dependency Inversion Principle (принцип инверсии зависимостей)
- Суть: высокоуровневые модули не должны зависеть от низкоуровневых; оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей; детали — от абстракций.
- Нарушение:
```java
class OrderService {
private final EmailNotifier notifier = new EmailNotifier(); // жёсткая зависимость
void placeOrder(Order o) { notifier.send(o); }
}
```
- Исправление (зависимость от интерфейса, DI):
```java
interface Notifier { void send(Order o); }
class EmailNotifier implements Notifier { public void send(Order o){ /* ... */ } }
class OrderService {
private final Notifier notifier;
OrderService(Notifier notifier) { this.notifier = notifier; } // внедряем зависимость
void placeOrder(Order o) { notifier.send(o); }
}
```
Теперь можно подменять реализацию (Email, SMS, Mock) без изменения OrderService.
Короткие рекомендации при проектировании order‑системы:
- Делите обязанности (SRP): отдельные классы для хранения, оплаты, уведомлений, расчёта скидок.
- Расширяйте через интерфейсы/стратегии (OCP).
- Разделяйте интерфейсы по ролям (ISP).
- Не привязывайтесь к конкретным реализациям; используйте DI/контейнеры (DIP).
- Избегайте подклассов, которые нарушают контракт базового класса; при сомнении выделяйте дополнительные интерфейсы (LSP).
Если нужно, могу привести более развёрнутые Java‑файлы (полные классы) для каждой иллюстрации.