Какую проблему решают принципы SOLID?

«Какую проблему решают принципы SOLID?» — вопрос из категории ООП, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

  • Поддерживать (менять одну часть, не ломая другие).
  • Расширять (добавлять новую функциональность с минимальными изменениями).
  • Тестировать (изолировать компоненты для unit-тестов).

Конкретные проблемы и решения на примере PHP:

  1. SRP (Принцип единственной ответственности): Решает проблему "божественного объекта", который делает всё.

    // ПЛОХО: Класс знает о логике пользователя, валидации и работе с БД.
    class User {
        public function validate(): bool { /* ... */ }
        public function saveToDatabase(): void { /* ... */ }
        public function sendWelcomeEmail(): void { /* ... */ }
    }
    // ХОРОШО: Ответственность разделена.
    class UserEntity { /* данные */ }
    class UserValidator { public function validate(UserEntity $user): bool { /* ... */ } }
    class UserRepository { public function save(UserEntity $user): void { /* ... */ } }
    class EmailService { public function sendWelcome(UserEntity $user): void { /* ... */ } }
  2. OCP (Принцип открытости/закрытости): Решает проблему, когда добавление новой функциональности требует модификации существующего, уже работающего кода.

    // ПЛОХО: Добавление нового типа отчета требует правки метода.
    class ReportGenerator {
        public function generate(string $type) {
            if ($type === 'pdf') { /* код PDF */ }
            elseif ($type === 'csv') { /* код CSV */ }
            // Добавление 'excel' => еще один elseif
        }
    }
    // ХОРОШО: Код закрыт для модификации, но открыт для расширения.
    interface ReportGeneratorInterface { public function generate(): string; }
    class PdfReport implements ReportGeneratorInterface { public function generate(): string { /* ... */ } }
    class CsvReport implements ReportGeneratorInterface { public function generate(): string { /* ... */ } }
    // Чтобы добавить ExcelReport, мы создаем новый класс, не трогая существующие.
  3. LSP (Принцип подстановки Барбары Лисков): Решает проблему неожиданного поведения программы при замене родительского класса его наследником. Наследник должен дополнять, а не изменять контракт родителя.

  4. ISP (Принцип разделения интерфейса): Решает проблему "толстых" интерфейсов, когда класс вынужден реализовывать методы, которые ему не нужны.

  5. DIP (Принцип инверсии зависимостей): Решает проблему жесткой привязки к конкретным реализациям, что затрудняет тестирование и замену компонентов.

    // ПЛОХО: Высокоуровневый модуль зависит от низкоуровневого.
    class OrderProcessor {
        private MySQLDatabase $database; // Прямая зависимость от конкретной БД
        public function __construct() { $this->database = new MySQLDatabase(); }
    }
    // ХОРОШО: Оба модуля зависят от абстракции.
    interface DatabaseConnectionInterface { public function query(string $sql): array; }
    class OrderProcessor {
        private DatabaseConnectionInterface $db;
        public function __construct(DatabaseConnectionInterface $db) { // Внедрение зависимости
            $this->db = $db;
        }
    }
    // Теперь OrderProcessor можно тестировать с Mock-объектом и легко переключать БД.

    В своей практике я применяю SOLID как основу для проектирования сервисов в Symfony, что напрямую влияет на качество и скорость разработки в долгосрочной перспективе.