Pull to refresh
6
0
Сергей Кольцов@kltsv

User

Send message

Я правильно понимаю, что вы не рассматриваете конкурентные инстансы одного скоупа? … Возможна ли такая ситуация на мобилке?

Через yx_scope можно создавать несколько скоупов одного типа, такой пример можно посмотреть в example.

Да, похожая ситуация возможно, но в контексте скоупов в приложении создание разных скоупов одного и того же типа — это скорее исключительный кейс, чем стандартная ситуация.

Если же не контролируем, то потенциально объекты могут использоваться где-то когда мы попытаемся родительский закрыть. 

Контроля в этот момент нет, потому что решение о закрытии родительского скоупа может принимать сущность, которая даже не знает о наличии дочерних.

И тут, по хорошему, объекты скоупа должны использоваться только когда скоуп доступен — либо внутри контекста этого скоупа (т.е. внутри зависимостей, принадлежащих этому скоупу), либо в UI, который подписан на изменение состояния скоупа.

Т.е. при закрытии скоупа его зависимости тоже должны перестать использоваться.

Но да, можно сохранить инстансы зависимостей куда-нибудь в переменные в другом контексте — тогда скоуп-то закроется, а работа с зависимостями может и не прекратиться. Но это пример неправильного использования зависимостей, про это есть правило в мануальном линтере.

Пока вот такое написал

Да, похожее решение я и имел в виду.

Типа у вас в примере создается холдер, потом асинхронно вызывается на нем метод create, а без этого никак?

Да, без этого никак, это одна из ключевых особенностей и отличий. Всегда нужно управлять ЖЦ скоупа через холдер. Это позволяет добиться того, что DI — это не какая-то глобальная статическая сущность без ЖЦ и существующая всегда. DI — это контейнер в конкретной области видимости, привязанный к конкретному ЖЦ фичи/процесса.

Мне что везде прокидывать этот холдер по цепочке? Это же неудобно. Для виджетов вы обертку сделали, а если это не в виджете? Например в каком-то кубите.

Предполагается, что все зависимости всегда поставляются через конструктор, в том числе и в кубит. Т.е. если кубит должен управлять каким-то скоупом через его холдер, то да, этот холдер должен быть передан в конструктор, причём желательно это должен быть интерфейс, который реализуется холдером. А в конструктор он передаётся внутри ScopeContainer. А ScopeContainer содержит другие зависимости этого же скоупа, и зависимости родительского скоупа — среди этих двух множеств и должен существовать тот холдер, который нужно прокинуть в кубит.

С классическим подходом я делал просто GetIt.I<MyClass>() и все.

Да, в GetIt можно просто сделать статический вызов GetIt.I<MyClass>(), но такой вызов чреват многими проблемами, как минимум это сложнее тестировать и контролировать, какие зависимости как друг с другом связываются.

В принципе похожего поведения можно добиться и в yx_scope, если создать  top-level ScopeHolder, проинициализировать его в main и обращаться к нему через force-unwrap: scopeHolder.scope!.someDep. Тогда можно будет в любом месте делать так же как и GetIt.I<MyClass>(), но все преимущества yx_scope тогда теряют свою пользу.

Глобально причины две:

1. Теряется null-safety. Если делать просто плоский набор контейнеров, тогда мы лишаем себя возможности описывать структуру, когда один контейнер должен существовать только в рамках другого. Это можно будет реализовать, но тогда придётся везде либо писать проверку на null для существования родительского скоупа, либо делать force-unwrap, но тогда null-safety полностью теряется.

2. Теряется возможность автоматически закрывать всё поддерево скоупов. Это нужно будет делать вручную. С деревом скоупов же можно у родительского скоупа сделать drop, и все его дочерние скоупы автоматически закроются вместе с ним.

Если я правильно понял вопрос, то такая возможность существует, достаточно типизировать зависимость нужным типом:

late final futureTypeDep 
  = dep<Future<Type>>(() => /* зависимость, возвращающая Future */)

И далее вы обращаетесь в futureTypeDep, при первом обращении фьюча начинает выполняться, и как только результат получен — он будет доступен всем пользователям этой зависимости.

Другой вопрос, что если есть Future-зависимости, то это значит, что даже в проинициализированном контейнере некоторые зависимости всё ещё, условно, “не готовы”, и их нужно предварительно дождаться. 

И чтобы решить эту ситуацию и дать возможность подготовить асинхронные зависимости до того, как контейнер станет доступен, мы сделали механизм initializeQueue — тогда инициализация асинхронной зависимости становится частью инициализации скоупа, и к моменту, когда скоуп создан, всего зависимости точно проинициализированы и готовы к работе.

Но вы правильно заметили, что не все классы внешних библиотек можно инстанцировать, но не инициализировать. Для таких случаев самое простое решение — написать собственный класс-обёртку, у которого есть методы init/dispose, в которых он создаёт и сохраняет нужный инстанс. И этот класс обёртку уже можно безболезненно использовать в DI.

Да, каждый раз писать такую обёртку — бойлерплейт, поэтому это одна из идей, которую мы уже рассматриваем как один из следующих шагов в yx_scope — добавить в библиотеку такой универсальный класс-обёртку, который можно будет использовать в initializeQueue.

Дерево скоупов имеет смысл, когда в приложении есть группы зависимостей под разные крупные фичи, жизненный цикл которых не зависит друг от друга — может совсем не пересекаться или наоборот накладываться друг на друга. Если такое происходит — линейная иерархия уже не подойдёт, потому что это означает, что одна фича будет зависеть от другой, но с точки зрения бизнес-логики фичи могут совсем не пересекаться.

Это может быть излишним усложнением для простых проектов, и поэтому я призываю аккуратно относиться к скоупам и не плодить их на всё подряд — действительно могут быть кейсы, где это излишне, и проще обойтись единственным контейнером с зависимостями.

Но на больших проектах с крупными фичами, каждая из которых может разрабатываться отдельной командой и иметь свой изолированный ЖЦ и набор зависимостей — выделить такую фичу в скоуп, который будет жить на равных правах со своими скоупами-сиблингами из дерева — это достаточно гибкая стратегия.

Information

Rating
Does not participate
Registered
Activity

Specialization

Specialist
Разработка мобильных приложений