В чем различия Service Locator и Dependency Injection?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Краткое резюме
Service Locator и Dependency Injection — это два паттерна управления зависимостями. Service Locator — это объект, который хранит и предоставляет экземпляры сервисов по требованию. Dependency Injection передаёт зависимости в конструктор или через методы. Несмотря на похожесть, они имеют принципиальные различия в архитектуре и тестируемости.
Service Locator
Service Locator — это паттерн, где централизованный объект управляет всеми зависимостями и предоставляет их компонентам по запросу.
Пример реализации
class ServiceLocator:
_services = {}
@classmethod
def register(cls, name: str, service):
cls._services[name] = service
@classmethod
def get(cls, name: str):
if name not in cls._services:
raise ValueError(f"Service {name} not found")
return cls._services[name]
# Регистрация сервисов
locator = ServiceLocator()
locator.register("database", Database())
locator.register("logger", Logger())
# Использование в компоненте
class UserRepository:
def __init__(self):
self.db = ServiceLocator.get("database")
self.logger = ServiceLocator.get("logger")
Характеристики
- Компонент самостоятельно ищет нужные зависимости
- Глобальное состояние (слабое место)
- Простая реализация для небольших проектов
- Скрытые зависимости (не видно из сигнатуры класса)
Dependency Injection
Dependency Injection передаёт зависимости извне, обычно через конструктор (Constructor Injection), метод (Setter Injection) или параметр функции.
Пример конструктора (Constructor Injection)
class UserRepository:
def __init__(self, db: Database, logger: Logger):
self.db = db
self.logger = logger
# Использование
database = Database()
logger = Logger()
repo = UserRepository(database, logger)
Пример Setter Injection
class UserRepository:
def __init__(self):
self.db = None
self.logger = None
def set_database(self, db: Database):
self.db = db
return self
def set_logger(self, logger: Logger):
self.logger = logger
return self
# Использование
repo = UserRepository()
repo.set_database(Database()).set_logger(Logger())
Сравнительная таблица
| Аспект | Service Locator | Dependency Injection |
|---|---|---|
| Источник зависимости | Компонент сам ищет | Передаётся извне |
| Видимость зависимостей | Скрытые (в коде) | Явные (в сигнатуре) |
| Тестируемость | Сложнее (нужно мокировать глобальный объект) | Проще (подставляешь mock в конструктор) |
| Связанность | Высокая (привязка к locator) | Низкая (только интерфейсы) |
| Производительность | Поиск во время выполнения | Знаем зависимости заранее |
Почему DI лучше Service Locator
1. Явные зависимости
# Service Locator — скрытые зависимости
class PaymentProcessor:
def process(self):
db = ServiceLocator.get("database") # Откуда это берётся?
logger = ServiceLocator.get("logger")
# DI — явные зависимости
class PaymentProcessor:
def __init__(self, db: Database, logger: Logger):
self.db = db
self.logger = logger
2. Тестируемость
# DI — просто передаём mock
class TestPaymentProcessor:
def test_process(self):
mock_db = MockDatabase()
mock_logger = MockLogger()
processor = PaymentProcessor(mock_db, mock_logger)
result = processor.process()
assert result.success
Практический пример
from typing import Protocol
class DatabasePort(Protocol):
def execute(self, query: str): ...
class UserService:
def __init__(self, db: DatabasePort):
self.db = db
def create_user(self, name: str) -> dict:
return self.db.execute(f"INSERT INTO users VALUES ({name})")
Заключение
DI — это современный стандарт, поддерживаемый всеми серьёзными фреймворками (FastAPI, Django). Service Locator — это anti-pattern. Используй DI для явности, тестируемости и масштабируемости.