Zenject - легковесный фреймворк для dependency injection, специально сделанный для Unity, чтобы упростить доставку объектов в нужные части кода.
Для этого Zenject использует контейнер (он же DiContainer). Это объект, который хранит все биндинги, создаёт объекты (при желании) и предоставляет одну и ту же зависимость в разные места.
Для того, чтобы настроить Zenject используется Context. Это ассет, который преднастраивает контейнер, зависимости которого интегрируются в Unity.
Контекст бывает двух видов:
- SceneContext - для зависимостей контекста сцены.
- ProjectContext - для глобальных зависимостей.
Сам биндинг зависимостей происходит в классе, который наследуется от Installer и MonoInstaller. Мы переопределяем метод InstallBindings и прописываем условия внедрения зависимостей. InstallBindings - это место, где стоится граф зависимостей. По сути, он является нашим Composite Root.
using UnityEngine;
using Zenject;
public class GameInstaller : MonoInstaller
{
public GameObject enemyPrefab;
public SomeComponent sceneComponent;
public override void InstallBindings()
{
// привязать интерфейс к реализации как синглтон
Container.Bind<IGameService>().To<GameService>().AsSingle();
// привязать конкретный инстанс (например, инспекторный компонент)
Container.BindInstance(sceneComponent).AsSingle();
// фабрика для создания врагов из префаба
Container.BindFactory<Enemy, Enemy.Factory>()
.FromComponentInNewPrefab(enemyPrefab);
// пул для часто создаваемых объектов (с использованием poolable API)
Container.BindMemoryPool<Bullet, Bullet.Pool>()
.WithInitialSize(20)
.FromComponentInNewPrefab(bulletPrefab);
}
}
После настройки биндингов, мы можем спокойно получить зависимость. Например, как получить этот биндинг:
Container.Bind<IGameService>().To<GameService>().AsSingle();
Использовать атрибут [Inject].
using UnityEngine;
using Zenject;
public class GameView : MonoBehaviour
{
private IGameService _gameService;
[Inject]
public void Construct(IGameService gameService)
{
_gameService = gameService;
}
private void Start()
{
_gameService.StartGame();
}
}
Таким образом, просто указав интерфейс и его реализацию, у нас прокидывается нужная зависимость. И не нужно использовать сложные шаблоны проектирования, всё работает под капотом.
Детально про возможности Zenject можно прочитать в официальной документации. Она довольно понятным языком описывает все возможности Zenject, в том числе и за пределами биндинга (фабрики, пулы, сигналы, код которых указан выше). Но я предпочитаю использовать только DI, чтобы не сильно зависеть от этого фреймворка.
Ссылка на документацию: https://github.com/modesttree/Zenject
🚀 Пост Guru Unity: @Minerope