Сравнение с 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. Некоторые из них немного отличаются. Это показывает, насколько всесторонне Кодеин рассматривает различные второстепенные случаи.

Затем, в BaseApplicationBaseModule), у нас есть эти модули, загруженные с использованием 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, который более упрощен, не уверен, поможет ли это сделать его более актуальным.

Надеюсь, этот пост будет вам полезен.