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

Что означает D в SOLID?

1.7 Middle🔥 251 комментариев
#Архитектура и паттерны

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

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

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

Что означает D в SOLID?

D в SOLID означает Dependency Inversion Principle (Принцип инверсии зависимостей). Это принцип проектирования, который гласит: высокоуровневые модули не должны зависеть от низкоуровневых модулей. Оба должны зависеть от абстракций.

Суть принципа

Депендency Inversion Principle состоит из двух частей:

  1. Высокоуровневые модули не должны зависеть от низкоуровневых модулей — оба должны зависеть от абстракций
  2. Абстракции не должны зависеть от деталей — детали должны зависеть от абстракций

Это инвертирует традиционный способ написания кода, где высокоуровневый код зависит от низкоуровневых деталей реализации.

Пример нарушения принципа

# ❌ Плохо — высокоуровневый код зависит от конкретной реализации
class UserRepository:
    def save(self, user):
        # Сохранение в PostgreSQL
        pass

class UserService:
    def __init__(self):
        self.repository = UserRepository()  # Жёсткая зависимость
    
    def create_user(self, name, email):
        user = {"name": name, "email": email}
        self.repository.save(user)

# Если захотим использовать MongoDB, придётся менять UserService!

Правильная реализация

# ✅ Хорошо — зависимость от абстракции
from abc import ABC, abstractmethod
from typing import Protocol

# Абстракция (интерфейс)
class IUserRepository(ABC):
    @abstractmethod
    def save(self, user: dict) -> None:
        pass

# Конкретные реализации
class PostgresUserRepository(IUserRepository):
    def save(self, user: dict) -> None:
        print(f"Saving to PostgreSQL: {user}")

class MongoUserRepository(IUserRepository):
    def save(self, user: dict) -> None:
        print(f"Saving to MongoDB: {user}")

# Высокоуровневый модуль зависит от абстракции
class UserService:
    def __init__(self, repository: IUserRepository):
        self.repository = repository  # Инъекция зависимости
    
    def create_user(self, name: str, email: str) -> None:
        user = {"name": name, "email": email}
        self.repository.save(user)

# Использование
if __name__ == "__main__":
    # Легко менять реализацию
    postgres_repo = PostgresUserRepository()
    service1 = UserService(postgres_repo)
    service1.create_user("John", "john@example.com")
    
    mongo_repo = MongoUserRepository()
    service2 = UserService(mongo_repo)
    service2.create_user("Jane", "jane@example.com")

Ещё лучше — использование Protocol (Python 3.8+)

from typing import Protocol

class UserRepository(Protocol):
    """Интерфейс репозитория пользователей"""
    
    def save(self, user: dict) -> None:
        ...

class PostgresUserRepository:
    """Конкретная реализация для PostgreSQL"""
    def save(self, user: dict) -> None:
        print(f"Saving to PostgreSQL: {user}")

class UserService:
    def __init__(self, repository: UserRepository):
        self.repository = repository
    
    def create_user(self, name: str, email: str) -> None:
        user = {"name": name, "email": email}
        self.repository.save(user)

# Работает благодаря структурной типизации (duck typing + type hints)
repo = PostgresUserRepository()
service = UserService(repo)

Преимущества Dependency Inversion Principle

  1. Слабая связанность — компоненты не знают о конкретных реализациях
  2. Тестируемость — легко подставить mock-объекты
class MockUserRepository(IUserRepository):
    def save(self, user: dict) -> None:
        pass  # Не делаем ничего, просто для теста

def test_user_service():
    mock_repo = MockUserRepository()
    service = UserService(mock_repo)
    service.create_user("Test", "test@example.com")
    # Тест пройдёт, БД не затронется
  1. Расширяемость — новые реализации легко добавляются без изменения существующего кода
  2. Переиспользуемость — высокоуровневый код не привязан к конкретным реализациям

Антипаттерны

# ❌ Создание зависимостей внутри функции
def process_order(order_id):
    db = PostgresDatabase()  # Жёсткая зависимость
    repository = UserRepository(db)
    # ...

# ✅ Передача зависимостей
def process_order(order_id, repository: UserRepository):
    # ...

Связь с другими принципами SOLID

  • Single Responsibility: каждый класс имеет одну ответственность
  • Open/Closed: открыт для расширения (новых реализаций), закрыт для модификации
  • Liskov Substitution: все реализации могут заменять одна другую
  • Interface Segregation: интерфейсы узкие и специфичные

Dependency Inversion — это ключевой принцип для чистого, тестируемого и масштабируемого кода.