Какие знаешь контексты Zenject?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Контексты в Zenject (Zenject / Extenject)
Я подробно расскажу о контекстах в Zenject (ныне известном как Extenject), так как это фундаментальная концепция для организации инъекции зависимостей в Unity, особенно в сложных проектах с несколькими сценами, подсистемами и асинхронной загрузкой.
Контекст — это контейнер, который определяет область видимости, время жизни и способ разрешения привязок зависимостей. Это граница, в которой зависимости регистрируются и разрешаются. Все контексты в Zenject иерархичны, что позволяет создавать сложные, модульные и эффективные архитектуры.
Основные типы контекстов
1. Scene Context (Контекст сцены)
Это самый распространённый и часто используемый контекст. Он привязан к конкретной сцене Unity.
- Назначение: Определяет зависимости, общие для всей сцены. Обычно это главный контекст, на который "опираются" все остальные в рамках сцены.
- Использование: Добавляется как компонент на GameObject на сцене (часто на пустой объект с именем, например,
SceneContext). В его Installer List добавляются инсталлеры с привязками. - Время жизни: Создаётся при загрузке сцены и уничтожается при её выгрузке.
- Важная настройка:
Parent Containers. Позволяет указать родительский контейнер (например,ProjectContext), чьи зависимости будут доступны в этой сцене.
2. Project Context (Контекст проекта)
Глобальный, "сквозной" контекст, существующий на протяжении всей жизни приложения.
- Назначение: Регистрация глобальных зависимостей (сервисов, менеджеров, конфигураций), которые должны быть доступны во всех сценах без пересоздания (синглтоны уровня приложения).
- Использование: Это префаб, автоматически создаваемый Zenject в папке
Resources. Он инициализируется при старте первой сцены и не уничтожается между загрузками сцен (DontDestroyOnLoad). - Пример: Сервис сохранения данных (
ISaveService), аудио-менеджер, настройки игры, фабрика сетевых запросов.
// В инсталлере ProjectContext
public class GlobalServicesInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<IAudioManager>().To<AudioManager>().FromNewComponentOnNewGameObject().AsSingle().NonLazy();
Container.Bind<IGameConfig>().FromScriptableObjectResource("GameConfig").AsSingle();
Container.BindInterfacesAndSelfTo<SaveService>().AsSingle();
}
}
3. GameObject Context (Контекст GameObject)
Позволяет создать локальный контейнер зависимостей для определённого GameObject и его дочерней иерархии.
- Назначение: Инкапсуляция логики и зависимостей сложного составного объекта (например, персонажа игрока, врага, UI-панели). Это основной инструмент для внедрения зависимостей в префабы.
- Использование: Добавляется как компонент на корневой GameObject префаба. Все компоненты на этом GameObject и его детях могут использовать зависимости, зарегистрированные в этом контексте.
- Иерархия: Родителем для
GameObjectContextобычно являетсяSceneContextили другойGameObjectContext.
4. Decorator Context
Специальный контекст. Позволяет "декорировать" (добавлять, переопределять) привязки из родительского контекста для дочернего GameObject. Это более гибкая альтернатива GameObjectContext для модификации зависимостей на определённом поддереве иерархии.
Как контексты взаимодействуют: Иерархия контейнеров
Ключевой принцип Zenject — иерархия контейнеров (Container Hierarchy). При поиске зависимости сначала проверяется локальный контейнер (например, GameObjectContext), затем его родитель (SceneContext), и далее вверх по цепочке до ProjectContext.
- Это позволяет:
* **Переопределять привязки**: Локально в `GameObjectContext` можно перепривязать интерфейс к другой реализации.
* **Создавать модульные системы**: `GameObjectContext` префаба "герой" ничего не знает о `SceneContext` игрового уровня, кроме договорённостей об интерфейсах.
* **Управлять временем жизни**: Зависимость, объявленная в `ProjectContext` как `AsSingle()`, будет существовать в единственном экземпляре на всё приложение. Та же самая привязка в `SceneContext` будет создавать новый экземпляр для каждой сцены.
// Пример: Переопределение в GameObjectContext
public class EnemyInstaller : MonoInstaller
{
[SerializeField] private Weapon _specificWeapon;
public override void InstallBindings()
{
// Локально для этого врага привязываем IWeapon к конкретному экземпляру.
// SceneContext больше не будет использоваться для разрешения IWeapon в этом поддереве.
Container.Bind<IWeapon>().FromInstance(_specificWeapon);
}
}
Практические советы по использованию
- Стандартный шаблон:
ProjectContext(глобальные сервисы) ->SceneContext(данные уровня) ->GameObjectContext(сложные префабы). - Избегайте
FindObjectOfTypeи синглтонов черезstatic: Все глобальные объекты должны регистрироваться вProjectContext. NonLazy()для критичных сервисов: Для сервисов, которые должны быть созданы сразу (например, загрузка конфигов), используйтеAsSingle().NonLazy().- Тестирование: Использование разных контекстов позволяет легко подменять реализации зависимостей для модульного и интеграционного тестирования.
Таким образом, грамотное использование контекстов Zenject — это основа для создания чистой, тестируемой и масштабируемой архитектуры в Unity-проектах, позволяющей эффективно управлять сложностью и зависимостями между различными модулями игры.