Ответ
В современных фреймворках вместо паттерна Singleton активно используется Dependency Injection (DI). Этот подход позволяет более гибко и прозрачно управлять зависимостями и жизненным циклом объектов.
Singleton часто считается анти-паттерном из-за следующих недостатков:
- Глобальное состояние: Затрудняет отладку и тестирование.
- Жесткая связанность (tight coupling): Компоненты напрямую зависят от конкретной реализации синглтона.
- Проблемы с тестированием: Сложно подменить (mock) синглтон в unit-тестах.
Dependency Injection решает эти проблемы, инвертируя контроль: вместо того чтобы компонент сам создавал или запрашивал зависимость, она передается ему извне (инжектируется) через конструктор, метод или свойство.
Пример в FastAPI:
FastAPI имеет встроенную систему DI, работающую через Depends
.
from fastapi import Depends, FastAPI
app = FastAPI()
# Определяем зависимость (например, сервис для работы с БД)
class DatabaseService:
def get_data(self) -> str:
return "Data from database"
# Фабрика, которая будет создавать и предоставлять экземпляр сервиса
def get_db_service():
# Здесь может быть логика создания одного экземпляра на запрос
return DatabaseService()
@app.get("/data/")
async def read_data(db: DatabaseService = Depends(get_db_service)):
# FastAPI автоматически вызовет get_db_service и передаст результат в 'db'
data = db.get_data()
return {"data": data}
Преимущества DI перед Singleton:
- Упрощение тестирования: Зависимость
get_db_service
можно легко подменить на тестовую версию. - Явное объявление зависимостей: Сигнатура функции четко показывает, что ей нужно для работы.
- Гибкое управление жизненным циклом: Можно настроить создание нового экземпляра на каждый запрос, или одного на все приложение.