← Назад к вопросам

Что такое O в SOLID?

2.0 Middle🔥 251 комментариев
#SOLID и паттерны проектирования

Комментарии (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 { }

Проблемы:

  1. Каждый раз, когда добавляется новый тип сотрудника, нужно менять SalaryCalculator
  2. При добавлении DevOps нужно открыть уже работающий класс и добавить новый else if
  3. Высокий риск сломать существующую логику
  4. Нарушается 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));
    }
}

Ключевые выводы

  1. Абстракция — основа — используй интерфейсы и абстрактные классы
  2. Программируй под интерфейс, не под реализацию — это основное правило
  3. Новая функциональность = новые классы, не модификация старых
  4. Полиморфизм + Dependency Injection = гибкая архитектура
  5. Баланс важен — не усложняй для предполагаемого расширения, которого может не быть
Что такое O в SOLID? | PrepBro