В огромном приложении Android с Clean Architecture много рутинной работы, связанной с отображением объектов между различными слоями. Хорошим решением будет автоматизация этого процесса.

В этой статье рассматривается опыт использования библиотеки MapStruct, которая помогает mapp-ить объекты.

Библиотека очень гибка в настройке. И позволяет минимизировать написание шаблонного кода.

Вот пример того, как реализовать gradle зависимости в вашем проекте.

plugins {
   kotlin("kapt") version "1.9.22"
}
dependencies {
   implementation("org.mapstruct:mapstruct:1.6.0")
   kapt("org.mapstruct:mapstruct-processor:1.6.0")
}

Нажмите Синхронизировать ваш проект и начнем с самого простого и элементарного примера. Допустим, у нас есть два объекта:

data class PersonResponse(
   val id: Long,
   val name: String,
   val birthday: String
)
data class Person(
   val id: Long,
   val name: String,
   val birthday: String,
)

Для создания mapper-a нам необходимо создать интерфейс:

@Mapper
interface MapperPerson {
  
    fun userResponseToUser(userResponse: PersonResponse): Person
  
companion object {
    val INSTANCE = Mappers.getMapper(MapperPerson::class.java)
   }
}

Вот и все! Mapping уже работает.

@Test
fun `test map PersonResponse to Person`() {
   val response = PersonResponse(1, "John", "2024.01.01")
   val person = MapperPerson.INSTANCE.personResponseToPerson(response)
   assert(person.name == "John")
}

Mapping вложенного объекта

Давайте рассмотрим немного более сложный случай.

data class PersonResponse(
   val id: Long,
   val name: String,
   val birthday: String,
   val educationResponse: EducationResponse
)

data class EducationResponse(
   val universityName: String
)

data class Person(
   val id: Long,
   val name: String,
   val birthday: String,
   val education: Education
)

data class Education(
   val universityName: String
)

Поэтому нам нужно изменить mapper следующим образом:

@Mapper(uses = [MapperEducation::class])
interface MapperPerson {
  
@Mapping(source = "userResponse.educationResponse", target = "education")
fun personResponseToPerson(userResponse: PersonResponse): Person

companion object {
     val INSTANCE = Mappers.getMapper(MapperPerson::class.java)
   }
}

@Mapper
interface MapperEducation {
  
   fun map(educationResponse: EducationResponse) : Education
  
}

И тест пройден.

@Test
fun `test map PersonResponse to Person`() {
    val response = PersonResponse(
        id = 1,
        name = "John",
        birthday = "2024.01.01",
        educationResponse = EducationResponse("University")
    )
    val person = MapperPerson.INSTANCE.personResponseToPerson(response)
    assert(person.name == "John")
}

В статье рассмотрены базовые случаи mapping-a с использованием библиотеки MapStruct. Более сложные случаи будут рассмотрены в следующих статьях. Спасибо за внимание!

Комментарии (0)