Встречается на 62% собеседований по Java

Что такое SOLID

SOLID — это акроним пяти принципов объектно-ориентированного проектирования, которые помогают создавать гибкие, масштабируемые и легко поддерживаемые системы. Эти принципы направлены на устранение типичных проблем проектирования, таких как жесткие зависимости между компонентами и сложность внесения изменений. Следование SOLID делает код более понятным, тестируемым и расширяемым.

Как это работает

Каждая буква акронима соответствует одному принципу:

  1. S — Single Responsibility Principle (SRP), принцип единственной ответственности. Класс должен иметь только одну причину для изменения, то есть выполнять только одну задачу. Например, если класс UserSettings отвечает и за изменение данных пользователя, и за проверку прав доступа, это нарушает SRP. Лучше вынести проверку прав в отдельный класс, чтобы каждый класс имел четкую зону ответственности.

  2. O — Open/Closed Principle (OCP), принцип открытости/закрытости. Программные сущности (классы, модули, функции) должны быть открыты для расширения, но закрыты для модификации. Это означает, что новую функциональность можно добавлять без изменения существующего кода, например, через наследование или композицию.

  3. L — Liskov Substitution Principle (LSP), принцип подстановки Барбары Лисков. Наследники должны дополнять, а не изменять поведение базового класса. Если подкласс изменяет контракт базового класса, это может привести к ошибкам при использовании полиморфизма.

  1. I — Interface Segregation Principle (ISP), принцип разделения интерфейса. Лучше иметь несколько узкоспециализированных интерфейсов, чем один «толстый» интерфейс, который вынуждает классы реализовывать ненужные им методы.

  2. D — Dependency Inversion Principle (DIP), принцип инверсии зависимостей. Модули верхнего уровня не должны зависеть от модулей нижнего уровня; оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей, а детали — от абстракций. Это уменьшает связанность и упрощает замену реализаций.

Пример

Рассмотрим нарушение SRP и его исправление. Исходный класс UserSettings совмещает проверку прав и изменение данных:

java
class UserSettings {
    void changeEmail(User user) {
        if (checkAccess(user)) {
            // Изменение email пользователя
        }
    }

    boolean checkAccess(User user) {
        // Проверка прав доступа
        return true;
    }
}

Здесь checkAccess — это отдельная ответственность, которую лучше вынести в отдельный класс, например, AccessManager:

java
class AccessManager {
    boolean checkAccess(User user) {
        // Проверка прав доступа
        return true;
    }
}

class UserSettings {
    private AccessManager accessManager;

    void changeEmail(User user) {
        if (accessManager.checkAccess(user)) {
            // Изменение email пользователя
        }
    }
}

Теперь каждый класс отвечает за свою задачу, что упрощает тестирование и поддержку.

Подводные камни

  • Слепое следование принципам без понимания контекста может привести к излишней фрагментации кода и увеличению сложности.
  • SRP не означает, что класс должен содержать только один метод; речь идет об одной зоне ответственности.
  • OCP часто реализуется через наследование, но композиция может быть более гибкой.
  • LSP нарушается, когда наследник выбрасывает новые исключения или изменяет предусловия/постусловия методов.
  • ISP требует баланса: слишком много мелких интерфейсов усложняет понимание, слишком мало — приводит к «жирным» интерфейсам.
  • DIP не означает, что нужно всегда использовать интерфейсы; иногда достаточно абстрактного класса или даже просто инверсии управления через внедрение зависимостей.

Когда использовать

SOLID применяется при проектировании архитектуры на этапе разработки, особенно в крупных проектах, где важна поддерживаемость и расширяемость. Эти принципы полезны при рефакторинге, написании тестов и работе в команде, так как они делают код предсказуемым и уменьшают конфликты при слиянии изменений.

Коротко

  • SOLID — пять принципов ООП: SRP, OCP, LSP, ISP, DIP.
  • SRP: один класс — одна ответственность.
  • OCP: расширение без модификации.
  • LSP: наследники не должны ломать поведение базового класса.
  • ISP: узкие интерфейсы вместо «толстых».
  • DIP: зависимость от абстракций, а не от конкретных реализаций.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы