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

К чему может привести нарушение принципов SOLID

3.0 Senior🔥 111 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

К чему может привести нарушение принципов SOLID

Нарушение SOLID приводит к хрупкому, трудноподдерживаемому коду, который дорого стоит разработчикам и компаниям. Разберемся с каждым нарушением.

1. Нарушение Single Responsibility (S)

Класс, отвечающий за несколько вещей:

// ПЛОХО: класс отвечает за БД, логику и отправку email
public class UserManager {
    
    public void createUser(String email, String password) {
        // 1. Валидация
        if (email.isEmpty()) throw new RuntimeException("Email required");
        
        // 2. Сохранение в БД
        String sql = "INSERT INTO users VALUES (...)";
        executeSQL(sql);
        
        // 3. Отправка письма
        sendWelcomeEmail(email);
        
        // 4. Логирование
        log("User created: " + email);
    }
}

Проблемы:

  • Если изменится логика БД, нужно менять класс
  • Если изменится API отправки email, нужно менять класс
  • Нельзя протестировать создание пользователя без реального email
  • Класс имеет слишком много причин для изменения

Последствия:

Проблема в коде отправки email
↓
Пришлось менять UserManager
↓
Сломался код валидации
↓
Пали все тесты
↓
Производительность упала

2. Нарушение Open/Closed (O)

Код, который требует модификации при изменении требований:

// ПЛОХО: нужно добавлять новые типы платежей прямо в класс
public class PaymentProcessor {
    
    public void processPayment(String paymentType, double amount) {
        if (paymentType.equals("CREDIT_CARD")) {
            processCreditCard(amount);
        } else if (paymentType.equals("PAYPAL")) {
            processPayPal(amount);
        } else if (paymentType.equals("BITCOIN")) {
            processBitcoin(amount);
        } else if (paymentType.equals("APPLE_PAY")) {
            processApplePay(amount);
        }
        // При каждом новом способе оплаты — менять код
    }
}

Проблемы:

  • Нарушение принципа: класс ЗАКРЫТ для расширения
  • Каждое изменение = риск сломать существующую логику
  • Тесты нужно переписывать
  • Код растёт и становится неуправляемым

Правильное решение (Open for Extension):

public interface PaymentGateway {
    void process(double amount);
}

public class CreditCardPayment implements PaymentGateway {
    @Override
    public void process(double amount) { /* ... */ }
}

public class PaymentProcessor {
    private Map<String, PaymentGateway> gateways;
    
    public void processPayment(String paymentType, double amount) {
        PaymentGateway gateway = gateways.get(paymentType);
        gateway.process(amount);  // Просто вызовом
    }
}

// Новый способ оплаты — просто добавляем класс
public class CryptoPayment implements PaymentGateway {
    @Override
    public void process(double amount) { /* ... */ }
}

3. Нарушение Liskov Substitution (L)

Подклассы, не поддерживающие контракт суперкласса:

// Неправильно:
public class Bird {
    public void fly() {
        System.out.println("Flying");
    }
}

public class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Penguins can't fly");
    }
}

// Использование:
public void makeBirdFly(Bird bird) {
    bird.fly();  // Вызывается для Penguin → исключение!
}

Проблемы:

  • Непредсказуемое поведение
  • Нельзя заменять подклассы
  • Тесты падают неожиданно
  • Код становится хрупким

Решение через правильную иерархию:

public interface Bird { }

public interface FlyingBird extends Bird {
    void fly();
}

public class Eagle implements FlyingBird {
    @Override
    public void fly() { /* ... */ }
}

public class Penguin implements Bird {
    // Не обещаем полёт
}

4. Нарушение Interface Segregation (I)

Большой интерфейс с методами, которые не всем нужны:

// ПЛОХО: мутный интерфейс
public interface Worker {
    void work();
    void sleep();
    void eat();
    void manageEmployee(Employee emp);
    void firePerson();
}

// Робот должен работать, но не может спать/есть/управлять
public class Robot implements Worker {
    @Override
    public void work() { /* OK */ }
    
    @Override
    public void sleep() {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public void eat() {
        throw new UnsupportedOperationException();
    }
    
    // Много заглушек!
}

Проблемы:

  • Заглушки (stub implementations)
  • Нарушение контракта интерфейса
  • Сложность для клиентского кода
  • Тесты становятся нестабильными

Правильное решение: раздели интерфейс:

public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public interface Sleepable {
    void sleep();
}

public class Robot implements Workable {
    @Override
    public void work() { /* OK */ }
}

public class Human implements Workable, Eatable, Sleepable {
    // Реализует только то, что нужно
}

5. Нарушение Dependency Inversion (D)

Высокоуровневые модули зависят от низкоуровневых:

// ПЛОХО: UserService напрямую создаёт MySQLDatabase
public class UserService {
    private MySQLDatabase db = new MySQLDatabase();
    
    public User getUser(int id) {
        return db.query("SELECT * FROM users WHERE id = " + id);
    }
}

public class MySQLDatabase {
    // Специфичный для MySQL код
}

Проблемы:

  • Нельзя тестировать UserService без настоящей БД
  • Если хотим перейти на PostgreSQL — переписываем UserService
  • Зависимость жёстко закодирована
  • Невозможно использовать mock для тестов

Правильное решение: инверсия зависимостей:

// Высокоуровневый модуль зависит от абстракции
public class UserService {
    private Database db;
    
    public UserService(Database db) {  // Инъекция
        this.db = db;
    }
    
    public User getUser(int id) {
        return db.query("SELECT * FROM users WHERE id = " + id);
    }
}

public interface Database {
    User query(String sql);
}

public class MySQLDatabase implements Database {
    @Override
    public User query(String sql) { /* ... */ }
}

public class PostgreSQLDatabase implements Database {
    @Override
    public User query(String sql) { /* ... */ }
}

// Тестирование:
public class MockDatabase implements Database {
    @Override
    public User query(String sql) {
        return new User(1, "Test");
    }
}

// Использование:
UserService service = new UserService(new MockDatabase());  // Тест
UserService prodService = new UserService(new MySQLDatabase());  // Продакшен

Итоговые проблемы нарушения SOLID

ПроблемаВлияние
Высокая стоимость поддержки+50% времени на изменения
Низкая переиспользуемостьДублирование кода
Сложность тестированияНизкий coverage, хрупкие тесты
Наращивание технического долгаЭкспоненциальный рост стоимости
Невозможность масштабированияАрхитектура ломается при добавлении функций
Высокий риск регрессииИзменение одного = поломка десяти
Демотивация разработчиковСложно работать с грязным кодом

Практический пример нарушений

// ЭТА МОНСТР-КЛАСС нарушает ВСЕ SOLID принципы:
public class OrderProcessor {
    
    // Мноооого ответственности
    public void processOrder(Order order) {
        // 1. Валидация (S)
        if (order.getItems().isEmpty()) {
            throw new RuntimeException("Empty");
        }
        
        // 2. Работа с БД (S)
        String sql = "INSERT INTO orders ...";
        executeSQL(sql);
        
        // 3. Платёж (S)
        if (order.getPaymentType().equals("VISA")) {
            processVisa(order.getAmount());
        } else if (order.getPaymentType().equals("AMEX")) {
            processAmex(order.getAmount());
        }  // O нарушение
        
        // 4. Email (S)
        sendEmail(order.getCustomer().getEmail());
        
        // 5. Логирование (S)
        log(order.getId());
    }
    
    // Нельзя тестировать (D)
    // Нельзя подменить БД (D)
    // Нельзя переиспользовать (L)
}

Вывод

Нарушение SOLID приводит к:

  • Дорогой разработке — много времени на изменения
  • Хрупкому коду — изменение = регрессии
  • Сложности тестирования — невозможно покрыть тестами
  • Невозможности масштабирования — архитектура ломается
  • Накоплению долга — со временем становится еще хуже

Следование SOLID — это инвестиция в будущее проекта, которая окупается многократно.

К чему может привести нарушение принципов SOLID | PrepBro