Вопрос или проблема
Учитывая, что у меня есть API модель, где все поля могут быть пустыми, и соответствующая доменная модель, где все поля обязательны:
data class MyApiModel(
val property1: Int?,
val property2: String?,
val property3: Boolean?,
)
data class MyDomainModel(
val property1: Int,
val property2: String,
val property3: Boolean,
)
Я хочу написать функцию сопоставления, которая будет конвертировать экземпляр API модели в доменную модель, проверяя, что все поля не нулевые. Я хочу, чтобы функция возвращала этот ModelMapperResult
, содержащий список ошибок валидации:
data class ModelMapperResult<T>(
val value: T,
val validationErrors: List<String>,
)
Я пытаюсь найти шаблон кода для этого, который удовлетворяет двум целям:
- Он должен быть достаточно эргономичным; код должен быть относительно простым для чтения и написания.
- Он должен быть безопасным, например, предпочитать умные приведения типов и конструкции, такие как
let
, двойному восклицательному знаку (!!
).
На данный момент я не нашел хорошего способа сделать это, поэтому я использую код сопоставления, который возвращает только первую найденную ошибку валидации:
fun MyApiModel.toDomainModel(): ModelMapperResult<MyDomainModel?> {
val property1 = property1 ?: return ModelMapperResult(null, listOf("Отсутствует property1"))
val property2 = property2 ?: return ModelMapperResult(null, listOf("Отсутствует property2"))
val property3 = property3 ?: return ModelMapperResult(null, listOf("Отсутствует property3"))
return ModelMapperResult(
value = MyDomainModel(
property1 = property1,
property2 = property2,
property3 = property3,
),
validationErrors = emptyList(),
)
}
Есть какие-нибудь идеи?
Ответ или решение
Для того чтобы эффективно валидировать несколько свойств модели API и собирать ошибки в Kotlin, можно воспользоваться функциональными возможностями языка и избегать использования конструкции с двойным восклицательным знаком (!!). Ниже представлен пример, как можно реализовать функцию маппинга с учетом ваших требований:
data class MyApiModel(
val property1: Int?,
val property2: String?,
val property3: Boolean?,
)
data class MyDomainModel(
val property1: Int,
val property2: String,
val property3: Boolean,
)
data class ModelMapperResult<T>(
val value: T?,
val validationErrors: List<String>,
)
fun MyApiModel.toDomainModel(): ModelMapperResult<MyDomainModel> {
val validationErrors = mutableListOf<String>()
// Проверка свойства property1
val safeProperty1 = property1 ?: run {
validationErrors.add("Отсутствует property1")
null
}
// Проверка свойства property2
val safeProperty2 = property2 ?: run {
validationErrors.add("Отсутствует property2")
null
}
// Проверка свойства property3
val safeProperty3 = property3 ?: run {
validationErrors.add("Отсутствует property3")
null
}
return if (validationErrors.isEmpty()) {
ModelMapperResult(
value = MyDomainModel(
property1 = safeProperty1!!, // Здесь !! безопасно, так как мы проверили на null выше
property2 = safeProperty2!!,
property3 = safeProperty3!!
),
validationErrors = emptyList()
)
} else {
ModelMapperResult(value = null, validationErrors = validationErrors)
}
}
Пояснение:
-
Сбор ошибок в список: Вместо немедленного возврата при нахождении первого пустого значения, мы собираем все ошибки в список
validationErrors
. -
Безопасность и читаемость: Используя конструкцию
run
, мы можем добавлять ошибки в список в случае нахождения значенияnull
, что делает код более читаемым и безопасным. -
Проверка на наличие ошибок: В конце функции мы проверяем, пуст ли список ошибок. Если он пуст, то создаем экземпляр
MyDomainModel
с использованием безопасных свойств. -
Возврат результата: Если ошибки были собраны, мы возвращаем
ModelMapperResult
сnull
в качестве значения, иначе возвращаем успешно созданный объект доменной модели.
Таким образом, вы получите удобную и безопасную функцию для преобразования моделей с полной проверкой на наличие значений и аккуратным сбором ошибок.