Сравнение с Koin and Dagger 2
Поскольку я уже изучал Koin и Dagger 2 для применения в многомодульных Android-проектах, в завершение своего служебного долга я не должен упускать Kodein. Это еще один популярный фреймворк для внедрения зависимостей в Android, работающий на Kotlin.
Примечание. Другой подобный фреймворк - Injekt, который заявлен как очень похожий на Kodein.
Давайте посмотрим, как это делается здесь.
Предположения проекта
Вот как мы создадим наш простой проект:
- Есть три действия, из которых
MainActivity
может запускатьFeatureOneActivity
илиFeatureTwoActivity
. - Каждому из действий прививается собственная зависимость.
- Каждая из зависимостей внедряется с одноэлементной сетью и репозиторием (т. Е. Одна и та же копия внедряется во все зависимости.
Модуляризация проекта
Для долгосрочной масштабируемости предположим, что мы разбили на модули на основе действий. Мы могли бы сделать это и в других формах, это просто для упрощения иллюстрации.
У нас будет:
- Модуль приложения, содержащий
MainActivity
. - Функциональный один модуль, содержащий
FeatureOneActivity
. - Модуль функции два, содержащий
FeatureTwoActivity
. - И, наконец, что не менее важно, базовый модуль, в котором хранятся все общие объекты одноэлементного класса, которые нужно внедрить.
Кодеин подход
В Kodein есть концепция module
, как в Koin и Dagger 2. Скорее, как Dagger 2, это класс, а не функция (в Koin).
В классе module
мы используем bind
, чтобы указать, как мы можем создать нашу зависимость.
val baseNetworkModule = Kodein.Module("BaseNetwork") { bind<BaseNetwork>() with singleton { BaseNetwork("...") } } val baseRepositoryModule = Kodein.Module("BaseRepository") { bind<BaseRepository>() with singleton { BaseRepository("...") } }
Мы могли бы привязать его как singleton
, factory
, multiton
, provider
и instance
. Некоторые из них немного отличаются. Это показывает, насколько всесторонне Кодеин рассматривает различные второстепенные случаи.
Затем, в BaseApplication
(в BaseModule
), у нас есть эти модули, загруженные с использованием Kodein
, и есть глобальная статическая переменная, хранящая их.
class BaseApplication: Application() { companion object { val kodein = Kodein { import(baseNetworkModule) import(baseRepositoryModule) } } }
Мы должны поместить эту kodein
глобальную статическую переменную в BaseModule
, поскольку нам понадобится доступ к ней позже из других модулей. Следовательно, в отличие от Коина, мы не можем поставить его на верхний AppModule
уровень. Вместо этого он похож на Dagger 2, где он нам нужен в BaseModule
.
Зависимости основной деятельности
В нашей основной деятельности у нас есть AppDependency
, который зависит от Network
и Repository
.
class AppDependent(val name: String, private val baseRepository: BaseRepository, private val baseNetwork: BaseNetwork)
При этом нам просто нужно объявить его module
, как показано ниже:
val mainModule = Kodein.Module("Main") { bind<AppDependent>() with multiton { title: String -> AppDependent(title, instance(), instance()) } }
Примечание. Причина, по которой мы используем здесь multiton
вместо singleton
, заключается в том, что ему нужен параметр.
multiton
похож на factory
, он может иметь параметр, с той разницей, что он предоставляет один и тот же объект, если задано то же значение параметра. Фабрика всегда генерирует новый объект, даже если задано то же значение параметра.
Чтобы связать этот модуль с объектом kodein
в BaseModule
, ниже показано, как мы его кодируем.
class MainActivity : AppCompatActivity(), KodeinAware { override val kodein = Kodein { extend(BaseApplication.kodein) import(mainModule) } private val appDependent: AppDependent by instance(arg = "aaa") }
Что сделано, поясняется ниже.
- Сделайте так, чтобы наша
MainActivity
реализовалаKodeinAware
интерфейс. - Затем в нем нам понадобится свойство
kodein
. - Мы могли бы использовать
kodein
для расширения глобальногоkodein
изBaseModule
. - Затем мы могли бы добавить к нему
mainModule
. - Затем мы могли бы создать наш
appDependent
, используя функциюinstance
(или, скорее,Kodein.instance
).
Итак, подход здесь аналогичен Dagger 2, где он имеет MainActivity
kodein
, который расширяет глобальный kodein
в BaseModule
. Это не совсем новый объект, как в BaseModule
компоненте Dagger 2.
При этом ему не нужно выгружать mainModule
при уничтожении MainActivity
, как это делает Коин, потому что локальный kodein
будет жить только в пределах MainActivity
.
Что касается всего вышеперечисленного, мы могли бы сделать то же самое для первой и второй функции.
Общий вид
В целом, Kodein похож на Dagger 2, где соответствующий модуль kodein
должен расширяться от глобального kodein
в BaseModule
.
Оттуда вы могли увидеть, как все модули связаны между собой. Это позволяет Kodein автоматически находить соответствующих иждивенцев и связывать их.
Если вы хотите увидеть пример кода, вот он:
Общее мнение о Кодеине
Это моя первая попытка использовать Kodein. Неплохой опыт. Хотя я изо всех сил пытался найти что-то, казалось бы, простое из документа. К счастью, в Stack Overflow есть люди, которые следят за запросом.
В части связывания модулей
Он больше похож на Dagger 2, чем на Koin, поскольку каждый модуль должен быть связан друг с другом с помощью kodein
.
С точки зрения инъекции
Это больше похоже на Koin, где можно передать некоторые параметры создаваемому объекту. Итак, это не совсем чистая инъекция зависимостей, это больше похоже на локатор сервисов.
С точки зрения простоты использования
Легче подобрать по сравнению с Dagger 2. Однако Koin проще и легче подобрать. Я думаю, что в целом у Кодейна слишком много вещей, которые нужно изучить, и их можно было бы объединить.
Например. multiton
мог быть просто singleton
с параметрами, а provider
мог быть просто factory
без параметров.
Что касается документации
В целом, похоже, есть подробная информация, хотя мне не удалось найти то, что я хотел, и мне нужно было использовать Stack Overflow и другие неофициальные источники, чтобы найти их.
Я думаю, что это все еще лучше объяснено, чем Dagger 2. Но мне нравится более простая документация Koin. Возможно, у Koin есть меньше вещей, которые нужно изучить, что делает его более простым и приятным для новичка.
Полностью оригинальная кодовая база Kotlin
Без генерации кода. Это хорошо, поскольку в нем используется функция языка Kotlin. Это улучшает время компиляции в целом, как и Koin. В этом смысле лучше, чем Dagger 2.
При этом нам также не нужен lateinit var
, но может быть private val
для нашей зависимости.
Проверка ошибок
Поскольку код не проверяется и не генерируется во время компиляции, ошибки обнаруживаются во время выполнения, где он выйдет из строя, если что-то сделано неправильно, как и Коин.
Поскольку он не генерирует код, как Dagger 2, и не выполняет проверку ссылок во время компиляции, проблемы не могут быть обнаружены во время компиляции. Итак, это довольно серьезная проблема для использования в большой стабильной разработке.
В целом, я думаю, что Кодейн находится между Dagger 2 и Koin. Следовательно, я считаю, что это недостаток.
Если кто-то хочет что-то надежное и не обращает внимания на сложность и дополнительные накладные расходы (например, время компиляции), предпочтение отдается Dagger 2. Если кто-то предпочитает что-то более простое и быстрое, предпочтительнее Koin.
Я также думаю, что Kodein просто вводит слишком много функций, которые можно было бы дополнительно сгруппировать и оптимизировать (например, Dagger 2 значительно сократил общие требуемые API-интерфейсы по сравнению с оригинальным Dagger).
Но теперь у нас есть Koin, который более упрощен, не уверен, поможет ли это сделать его более актуальным.
Надеюсь, этот пост будет вам полезен.