Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Что такое O в SOLID?
Определение
O — Open/Closed Principle (Принцип открытости/закрытости)
Класс должен быть:
- Открыт для расширения — можно добавлять новую функциональность
- Закрыт для модификации — существующий код не должен меняться
Это позволяет добавлять новую функциональность БЕЗ риска сломать существующий код.
Плохой пример (нарушение принципа)
// Есть класс для расчёта зарплаты
public class SalaryCalculator {
public double calculate(Employee employee) {
// Для каждого нового типа сотрудника нужно менять этот код!
if (employee instanceof Developer) {
return ((Developer) employee).getBaseSalary() * 1.5;
} else if (employee instanceof Manager) {
return ((Manager) employee).getBaseSalary() * 2.0;
} else if (employee instanceof Designer) {
return ((Designer) employee).getBaseSalary() * 1.3;
}
return employee.getBaseSalary();
}
}
public class Developer extends Employee { }
public class Manager extends Employee { }
public class Designer extends Employee { }
Проблемы:
- Каждый раз, когда добавляется новый тип сотрудника, нужно менять SalaryCalculator
- При добавлении DevOps нужно открыть уже работающий класс и добавить новый else if
- Высокий риск сломать существующую логику
- Нарушается Single Responsibility — класс отвечает за все типы расчётов
Хороший пример (соответствие принципу)
// Интерфейс определяет контракт
public interface Employee {
double calculateSalary();
}
// Каждый тип реализует свой расчёт
public class Developer implements Employee {
private double baseSalary;
@Override
public double calculateSalary() {
return baseSalary * 1.5;
}
}
public class Manager implements Employee {
private double baseSalary;
@Override
public double calculateSalary() {
return baseSalary * 2.0;
}
}
public class Designer implements Employee {
private double baseSalary;
@Override
public double calculateSalary() {
return baseSalary * 1.3;
}
}
// Новый тип? Просто добавляем реализацию, БЕЗ изменений SalaryCalculator
public class DevOps implements Employee {
private double baseSalary;
@Override
public double calculateSalary() {
return baseSalary * 1.8;
}
}
// Калькулятор ЗАКРЫТ для модификации
public class SalaryCalculator {
// Работает с ЛЮБЫМ Employee, не нужны проверки типов
public double calculate(Employee employee) {
return employee.calculateSalary();
}
}
Механизмы реализации O в SOLID
1. Абстракция (Interfaces/Abstract Classes)
// Абстрактный класс определяет контракт
public abstract class PaymentProcessor {
abstract void process(Payment payment);
}
// Каждый конкретный процессор расширяет функциональность
public class StripeProcessor extends PaymentProcessor {
@Override
void process(Payment payment) {
// Логика Stripe
}
}
public class PayPalProcessor extends PaymentProcessor {
@Override
void process(Payment payment) {
// Логика PayPal
}
}
// Клиент работает с абстракцией, не зная о конкретной реализации
public class OrderService {
private PaymentProcessor processor;
public void payOrder(Order order) {
processor.process(order.getPayment());
}
}
2. Полиморфизм
public interface ReportGenerator {
void generate(Data data);
}
// Расширяем функциональность добавлением новых классов
public class PDFReportGenerator implements ReportGenerator {
@Override
public void generate(Data data) {
// Генерируем PDF
}
}
public class ExcelReportGenerator implements ReportGenerator {
@Override
public void generate(Data data) {
// Генерируем Excel
}
}
public class HTMLReportGenerator implements ReportGenerator {
@Override
public void generate(Data data) {
// Генерируем HTML
}
}
public class ReportService {
private ReportGenerator generator;
public void createReport(Data data) {
generator.generate(data);
}
}
3. Composition и Dependency Injection
// Вместо наследования передаём зависимость
public class UserValidator {
private EmailValidator emailValidator;
private PhoneValidator phoneValidator;
// Инжектируем валидаторы — не жёстко связаны с конкретной реализацией
public UserValidator(EmailValidator emailValidator, PhoneValidator phoneValidator) {
this.emailValidator = emailValidator;
this.phoneValidator = phoneValidator;
}
public boolean validate(User user) {
return emailValidator.isValid(user.getEmail()) &&
phoneValidator.isValid(user.getPhone());
}
}
// Можем подать любую реализацию валидатора
public interface EmailValidator {
boolean isValid(String email);
}
public class SimpleEmailValidator implements EmailValidator {
@Override
public boolean isValid(String email) {
return email.contains("@");
}
}
public class RegexEmailValidator implements EmailValidator {
@Override
public boolean isValid(String email) {
return email.matches("[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}");
}
}
4. Шаблонный метод (Template Method Pattern)
// Абстрактный класс определяет структуру, детали определяют подклассы
public abstract class DatabaseConnection {
public final void connect() {
openConnection();
authenticate();
initializeProperties();
}
protected abstract void openConnection();
protected abstract void authenticate();
protected abstract void initializeProperties();
}
public class MySQLConnection extends DatabaseConnection {
@Override
protected void openConnection() { /* MySQL логика */ }
@Override
protected void authenticate() { /* MySQL логика */ }
@Override
protected void initializeProperties() { /* MySQL логика */ }
}
public class PostgreSQLConnection extends DatabaseConnection {
@Override
protected void openConnection() { /* PostgreSQL логика */ }
@Override
protected void authenticate() { /* PostgreSQL логика */ }
@Override
protected void initializeProperties() { /* PostgreSQL логика */ }
}
Практический пример: System уведомлений
// Плохо — нарушение Open/Closed
public class NotificationService {
public void notify(String message, String type) {
if (type.equals("email")) {
sendEmail(message);
} else if (type.equals("sms")) {
sendSMS(message);
} else if (type.equals("push")) {
sendPush(message);
}
// Добавить Slack? Нужно открыть класс и менять его
}
}
// Хорошо — соответствует Open/Closed
public interface Notifier {
void notify(String message);
}
public class EmailNotifier implements Notifier {
@Override
public void notify(String message) {
// Email логика
}
}
public class SMSNotifier implements Notifier {
@Override
public void notify(String message) {
// SMS логика
}
}
public class PushNotifier implements Notifier {
@Override
public void notify(String message) {
// Push логика
}
}
// Добавить Slack? Просто создаём SlackNotifier, никаких изменений
public class SlackNotifier implements Notifier {
@Override
public void notify(String message) {
// Slack логика
}
}
public class NotificationService {
private final List<Notifier> notifiers;
public NotificationService(List<Notifier> notifiers) {
this.notifiers = notifiers;
}
public void notifyAll(String message) {
// Работает с ЛЮБЫМИ notifier'ами
notifiers.forEach(notifier -> notifier.notify(message));
}
}
Ключевые выводы
- Абстракция — основа — используй интерфейсы и абстрактные классы
- Программируй под интерфейс, не под реализацию — это основное правило
- Новая функциональность = новые классы, не модификация старых
- Полиморфизм + Dependency Injection = гибкая архитектура
- Баланс важен — не усложняй для предполагаемого расширения, которого может не быть