Ближе к концу 2017 года некоторые разработчики iZettle собрались вместе и начали обсуждать возможность корпоративной разработки с Kotlin. Моя команда запускала новый проект, и все мы думали, что это будет хорошая возможность попробовать свои силы в Kotlin.
Наши системы эффективно используют JDBI и Dropwizard, и мы не испытывали особого желания слишком сильно отклоняться от этого шаблона. Вы придерживаетесь того, что знаете, особенно когда начинаете изучать новый язык. Мы пошли осторожно.
Приложение Dropwizard в iZettle в основном состоит из трех частей: класса приложения, класса конфигурации и всего остального. Класс приложения быстро раздувается строкой за строкой создания экземпляров, переплетается с новыми экземплярами менеджеров и ресурсов. Каждый экземпляр тщательно объявляется за другим, создавая точный каскад экземпляров, один из которых вставляется в другой. Среди этих экземпляров были наши объекты JDBI, наши DAO (объект доступа к данным). А их было много.
Возможно, вы уже узнаете проблему и даже имеете готовое решение. Конечно, мы могли бы представить Guice, облегченный фреймворк Google для внедрения зависимостей, вместе с его мешаниной адаптеров для Dropwizard, и попытаться заставить все это работать с Kotlin. Нам это не понравилось. Вместо этого мы решили не усложнять задачу и перейти на Kotlin.
Kotlin вводит несколько новых концепций и связанных с ними ключевых слов, на осознание которых нужно время. Когда мы обнаружили inline
и reified
, мы были близки к решению.
Ключевое слово inline
указывает компилятору Kotlin буквально скопировать ваш код в то место, откуда он вызывается. Это позволяет вам оптимизировать собственный код до того, как это сделает компилятор, немного подстроив его под вашу волю. Вместе с inline
мы обнаружили reified
, который позволяет нам получить доступ к фактическому типу параметра универсального типа. Чтобы что-то было reified
, оно также должно быть inline
.
Мы создали новый класс для проксирования всех взаимодействий с JDBI. Мы назвали это DataOperations
. При запуске класс сканирует любые DAO (определяемые тем, что они находятся в конкретном пакете и наследуют интерфейс Dao
). Для этого мы использовали библиотеку Reflections, но поскольку она еще явно не поддерживает Kotlin, мы сохраняем объекты Java Class
, которые она создает.
companion object { val daoTypes: List<Class<out Dao>> = Reflections("com.izettle.db", MyApp::class.java.classLoader) .getSubTypesOf(Dao::class.java).toList() }
Теперь, когда у нас есть список обнаруженных классов DAO, мы можем использовать JDBI для их создания всякий раз, когда создается экземпляр DataOperations
, передавая объект Jdbi
. Это делается только один раз из основного класса приложения Dropwizard.
typealias DaoMap = Map<KClass<out Dao>, Dao> val daos: DaoMap = daoTypes.map { it.kotlin to jdbi.onDemand(it) }.toMap()
Затем, используя inline
и reified
, мы создали способ для других классов доступа к выбранному ими DAO. Мы назвали это use()
.
inline fun <reified T : Dao> use() = daos[T::class] as T
Это позволило нам использовать единственный экземпляр DataOperations
, объявленный один раз в классе приложения и совместно используемый со всеми другими ресурсами, менеджерами и т. Д., Которым необходим доступ к базе данных. Благодаря Kotlin и reified
эти другие классы могут получать доступ к DAO либо путем непосредственного указания желаемого типа, либо путем разрешения Kotlin делать вывод о типе.
// specify the desired Dao type val orderDao = dataOperations.use<OrderDao>() // infer the Dao type fun publish(orderDao: OrderDao) = doSomething(orderDao) publish( dataOperations.use() )
Мы продолжали использовать этот шаблон для добавления поддержки транзакций в DataOperations
. Для тестирования мы добавили класс MockingDataOperations
, который создает имитацию каждого DAO и содержит утилиты для сброса и программирования имоков.
Переход на использование DataOperations
сократил шаблонный код и упростил логику доступа к базе данных во всем приложении, сведя знания о JDBI в один класс.
Это только первая остановка в пути, который мы начали, когда решили принять Котлин. Мы уже с нетерпением ждем следующей остановки - следующего ага-момента.