Как стать автором
Обновить

Комментарии 14

Вот это:

void Start()
{
    // Injects dependencies
    Composition.Shared.BuildUp(this);
}

как будто бы не очень удобно - хочется чтобы был какой-то factory метод для инстаншиэйта, который бы инициализировал все монобэхи на префабе. типа _composition.Instantiate(prefab);

и внутри было что-то типа

public GameObject Instantiate(GameObject go)
{
    var result = GameObject.Instantiate(go);
    foreach (var innerMonoBeh in GetAllInnerMonobehs(go))
    {
        BuildUp(innerMonoBeh);
    }
    return result;
}

и ещё лучше инжектить не через проперти, а через метод, типа

public class Clock : MonoBehaviour|
{
    private IClockService _clockService = null!;

    [Dependency]
    public void Inject(IClockService clockService)
    {
        _clockService = clockService;
    }
}

BuildUp(Clock clock) принимает аргументом экземпляр конкретного типа (не базового). И в зависимости от этого типа выполняет внедрение набора зависимостей.

Вы можете использовать несколько Builder<>() что бы создать несколько перегрузок метода BuildUp(...). Но это всегда будет определенный набор, который соответствует набору изBuilder<>(). Это сделано потому что сгенерировать BuildUp() для всех типов объектов на этапе компиляции невозможно. Этим Pure.DI отличается от классическим библиотек DI. Pure.DI на этапе компиляции должен знать какие проверки выполнять и какие методы BuildUp(...)сгенерировать.

foreach (var innerMonoBeh in GetAllInnerMonobehs(go))
    {
        // вот тут бы тогда кодогенерить switch на все поддерживаемые монобехи,
        // для которых builder зарегистрирован 
       switch (innerMonoBeh)
        {
            case Clock clock:
                   BuildUp(clock);
                  break;
             default: break;
        }
 }

Не совсем понял зачем нужен этот метод, если его можно заменить на:

Composition.Shared.BuildUp(obj);

Если вдруг не будет нужной настройки, то не будет метода "BuildUp" и будет ошибка компиляции. Все разумно и безопасно

с методом Start, который вызывает инициализацию класса, вы как-будто бы нарушаете свой принцип - отсутствие зависимости на фреймворк DI.

По-моему стрёмно, когда каждый монобех сам себя инициализирует - ваш Composition.Shared превращается в какой-то сервис локатор в таком случае. С инстаншиэйтом через фабрики (как сделано во всех di фреймворках на юнити) - можно перейти на pure.di просто изменив атрибут с [Inject] на [Dependency] и не нужно ничего дописывать (ну кроме фабрики).

ещё без Start (а с инстаншиэйтом только через фабрику) можно уйти от .Builder()

просто все монобехи у которых есть [Dependency] дописывать в switch. Хотя не очень понятно, что мешает и сейчас генерить BuildUp для них автоматически...

То есть отличие от других контейнеров это кодогенерация?
Если это самоподнимаемый объект, и мы используем инициализацию в Start и имеем его как зависимость, не проще ли тогда просто использовать его как сервис локатор?

public IClockService ClockService { private get; set; }
void Start()
{ // Injects dependencies
ClockService = Composition.Shared.Resolve<IClockService>();
}

Еще хочется отметить, что по сути система инициализации через префабы и Scriptable Objects - это де факто встроенная в Unity DI система.
Можно просто использовать SO как сервисы (или адаптеры сервисов). У них нет своего поведения в рамках ЖЦ юнити, но поведение в смысле методов они вполне могут иметь.

Вообще жаль, что у юнити нет каких нибудь хаков на создание объектов, чтобы можно было снаружи подключить инъекции без модификации объекта или метода инициализации

Вообще жаль, что у юнити нет каких нибудь хаков на создание объектов, чтобы можно было снаружи подключить инъекции без модификации объекта или метода инициализации

Полностью с вами согласен

 не проще ли тогда просто использовать его как сервис локатор

Как сервис локатор работать не будет, Pure.DI - антипод сервис локатору. Только зарегистрированные корни можно получать, так как только для них есть код создания.

То есть отличие от других контейнеров это кодогенерация?

Это отличие порождает множество других. Есть свои плюсы и минусы.

Насколько Я понимаю, сервис локатор отличается от DI контейнером только направлением связи (и, из-за этого, сложностью.

DI поставляет зависимости снаружи, благодаря чему объект в идеале не знает про DI, соответственно не тащит зависимостей. Конечно в идеальном случае с конструктором. Если есть атрибуты и инъекция в метод/поле - это уже небольшая связь. Тут объект знает только про зависимость.

Сервис локатор - это точка доступа, у которой объект сам запрашивает нужные ему типы. Получается что объект знает про зависимость и сам сервис локатор.

То есть фактически DI содержит в себе сервис локатор и функции у них те же - возврат экземпляра зависимости

Зависимость и ее тип тоже настраивается снаружи (обычно это новый инстанс, единственный инстанс по типу синглтона или какие то специальные инстансы со специальным ЖЦ, например кешируемые)

Я разделяю вашу точку зрения. DI контейнер можно использовать как ServiceLocator. И чаще всего он так и и используется. В альтернативном, идеальном случае приложение, в самом начале выполнения, должно использовать DI контейнер чтобы получить один инстанс (корень композиции) на все приложение и больше не обращаться к DI.

В Unity - инфраструктура занимается созданием объектов сценариев. В методе инициализации Start() мы вызываем:

Composition.Shared.BuildUp(this);

Этот код передает в метод BuildUp объект конкретного (и даже не базового) типа. Т.е. мы просим инфраструктуру проставить объекту конкретного типа его зависимости. И это точно не ServiceLocator, так как объект не вызывает методов типа T GetServive<T>() и может запросить там любой T.

Это сделано в силу особенностей Unity.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории