Как вы применяли принципы SOLID в своей практике?

«Как вы применяли принципы SOLID в своей практике?» — вопрос из категории Основы программирования, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Применял SOLID для создания поддерживаемого и тестируемого кода. Каждый принцип решает конкретные проблемы:

  1. Single Responsibility (SRP) – Класс/функция отвечает за одну задачу. Например, разделил логику валидации данных и их сохранения в разные классы.
  2. Open/Closed (OCP) – Код открыт для расширения, но закрыт для модификации. Использовал абстрактные классы или интерфейсы, чтобы добавлять новое поведение через наследование или композицию, не меняя существующий код.
  3. Liskov Substitution (LSP) – Объекты базового класса можно заменять объектами производных классов без изменения корректности программы. Следил, чтобы переопределенные методы в наследниках не ужесточали предусловия и не ослабляли постусловия.
  4. Interface Segregation (ISP) – Много специализированных интерфейсов лучше одного общего. Вместо одного «толстого» интерфейса IReport создал IPrintable, IExportable, ISavable.
  5. Dependency Inversion (DIP) – Модули верхнего уровня не зависят от модулей нижнего уровня. Оба зависят от абстракций. Внедрял зависимости через конструктор или метод.

Практический пример DIP:

from abc import ABC, abstractmethod

# Абстракция (интерфейс)
class NotificationService(ABC):
    @abstractmethod
    def send(self, message: str):
        pass

# Модуль нижнего уровня (деталь)
class EmailService(NotificationService):
    def send(self, message: str):
        print(f"Sending email: {message}")

# Модуль верхнего уровня (логика)
class UserNotifier:
    def __init__(self, notifier: NotificationService):  # Зависим от абстракции
        self._notifier = notifier

    def notify_user(self, user_data: dict):
        message = f"Hello, {user_data['name']}!"
        self._notifier.send(message)  # Используем абстракцию

# Использование
email_service = EmailService()
notifier = UserNotifier(email_service)  # Внедряем конкретную реализацию
notifier.notify_user({"name": "Alice"})

Результат: Код стал менее связанным, его проще тестировать (можно подменить NotificationService на мок) и расширять (добавить SmsService).