当前位置: 代码迷 >> java >> 如何使用泛型和可能的反射来减少代码
  详细解决方案

如何使用泛型和可能的反射来减少代码

热度:17   发布时间:2023-07-31 11:48:02.0

我有DTO对象,然后我需要动态地对对象集合进行排序,有点像数据库ORDER BY,但是我没有数据库可以使查询真正完成实际工作(我知道,但这就是我所拥有的今天...)。

在我看来,StatusComparator和TypeComparator应该能够被重新编写为完全通用的,并且可能还利用了反射,因此我不需要为我的API中的每个对象编写一个(该服务还有三个)而且它真的很重复)。

一旦理解了正确的编写方式,我计划将比较器提取到自己的库中,以便与公司中的其他部门共享该模式,以使他们的代码更易于编写。

这段代码在Kotlin中,因此我们真的想尽可能地专注于该实现。

DTO的:

@Table("type")
data class TypeObject(

    @get:NotNull
    @PrimaryKey
    @JsonProperty("id") val id: String,

    @get:NotNull
    @Column("type")
    @JsonProperty("type") val type: String,

    @Column("is_deleted")
    @JsonProperty("isDeleted") val isDeleted: Boolean? = null
)

@Table("status")
data class StatusObject(
    @get:NotNull
    @PrimaryKey
    @JsonProperty("id") val id: String,

    @get:NotNull
    @JsonProperty("status") val status: String,

    @Column("is_deleted")
    @JsonProperty("isDeleted") val isDeleted: Boolean? = null
)

Comparatoes:

@Component
class StatusComparator<T : StatusObject> {
    fun buildComparator(
        field: String,
        asc: Boolean
    ): Comparator<T> {
        return if (asc) {
            compareBy {
                getField(field, it)
            }
        } else {
            compareByDescending {
                getField(field, it)
            }
        }
    }

    private fun getField(
        field: String,
        it: StatusObject
    ): Comparable<*>? {
        return when (field.toLowerCase()) {
            "id" -> it.id
            "status" -> it.status
            else -> it.isDeleted
        }
    }
}

@Component
class TypeComparator<T : TypeObject> {
    fun buildComparator(
        field: String,
        asc: Boolean
    ): Comparator<T> {
        return if (asc) {
            compareBy {
                getField(field, it)
            }
        } else {
            compareByDescending {
                getField(field, it)
            }
        }
    }

    private fun getField(
        field: String,
        it: TypeObject
    ): Comparable<*>? {
        return when (field.toLowerCase()) {
            "id" -> it.id
            "type" -> it.type
            else -> it.isDeleted
        }
    }
}

我的类型服务中的样本用法:

@Service
class TypeApiServiceImpl(
    private val repo: TypeRepository,
    private val sortListBuilder: SortListBuilder,
    private val customComparator: TypeComparator<TypeObject>
) : TypeApiService {

    override fun get(
        sort: String,
        filterId: UUID,
        filterType: String,
        filterIsDeleted: Boolean
    ): Mono<DocumentTierModels> {
        return if (filterId != UUID.fromString("00000000-0000-0000-0000-000000000000")) {
            this.getTypeById(filterId)
        } else {
            val objects = this.getTypeByFilter(filterType, filterIsDeleted)

            if (sort != "null") {
                this.getSortedTypes(sort, objects)
            } else {
                TypesModels(objects, MetaModel(null, listOf())).toMono()
            }
        }
    }

    private fun sortObject(
        objects: List<TypeObject>,
        sortItems: List<String>
    ): List<TypeObject> {
        when (sortItems.count()) {
            1 -> {
                val fieldAndDirection1 = sortItems[0].split(',')

                return objects
                    .sortedWith(customComparator.buildComparator(fieldAndDirection1[0], fieldAndDirection1[1] == "asc"))
            }
            2 -> {
                val fieldAndDirection1 = sortItems[0].split(',')
                val fieldAndDirection2 = sortItems[1].split(',')

                return objects
                    .sortedWith(
                        customComparator.buildComparator(fieldAndDirection1[0], fieldAndDirection1[1] == "asc")
                            .then(customComparator.buildComparator(fieldAndDirection2[0], fieldAndDirection2[1] == "asc"))
                    )
            }
            3 -> {
                val fieldAndDirection1 = sortItems[0].split(',')
                val fieldAndDirection2 = sortItems[1].split(',')
                val fieldAndDirection3 = sortItems[2].split(',')

                return objects
                    .sortedWith(
                        customComparator.buildComparator(fieldAndDirection1[0], fieldAndDirection1[1] == "asc")
                            .then(customComparator.buildComparator(fieldAndDirection2[0], fieldAndDirection2[1] == "asc"))
                            .then(customComparator.buildComparator(fieldAndDirection3[0], fieldAndDirection3[1] == "asc"))
                    )
            }
            else -> {
                return objects
            }
        }
    }
}

我的状态服务中的用法示例:

@Service
class StatusesApiServiceImpl(
    private val repo: StatusRepository,
    private val sortListBuilder: SortListBuilder,
    private val customComparator: StatusComparator<StatusObject>
) : StatusesApiService {

    override fun get(
        sort: String,
        filterId: UUID,
        filterStatus: String,
        filterIsDeleted: Boolean
    ): Mono<StatusModels> {
        return if (filterId != UUID.fromString("00000000-0000-0000-0000-000000000000")) {
            this.getStatusById(filterId)
        } else {
            val objects = this.getStatusByFilter(filterStatus, filterIsDeleted)

            if (sort != "null") {
                this.getSortedStatuses(sort, objects)
            } else {
                StatusModels(objects, MetaModel(null, listOf())).toMono()
            }
        }
    }

    private fun getSortedStatuses(
        sort: String,
        objects: List<StatusObject>
    ): Mono<StatusModels> {
        var objects1 = objects
        var sortList: MutableList<String> = mutableListOf()
        val sortItems = sortListBuilder.getSortItems(sort)

        if (sortItems != null) {
            sortList = sortListBuilder.buildSortList(sortItems)
            objects1 = this.sortObject(objects1, sortItems)
        }

        val meta = MetaModel(null, sortList)

        return StatusModels(objects1, meta).toMono()
    }

    private fun sortObject(
        objects: List<StatusObject>,
        sortItems: List<String>
    ): List<StatusObject> {
        when (sortItems.count()) {
            1 -> {
                val fieldAndDirection1 = sortItems[0].split(',')

                return objects
                    .sortedWith(customComparator.buildComparator(fieldAndDirection1[0], fieldAndDirection1[1] == "asc"))
            }
            2 -> {

                val fieldAndDirection1 = sortItems[0].split(',')
                val fieldAndDirection2 = sortItems[1].split(',')

                return objects
                    .sortedWith(customComparator.buildComparator(fieldAndDirection1[0], fieldAndDirection1[1] == "asc")
                        .then(customComparator.buildComparator(fieldAndDirection2[0], fieldAndDirection2[1] == "asc")))
            }
            3 -> {
                val fieldAndDirection1 = sortItems[0].split(',')
                val fieldAndDirection2 = sortItems[1].split(',')
                val fieldAndDirection3 = sortItems[2].split(',')

                return objects
                    .sortedWith(customComparator.buildComparator(fieldAndDirection1[0], fieldAndDirection1[1] == "asc")
                        .then(customComparator.buildComparator(fieldAndDirection2[0], fieldAndDirection2[1] == "asc"))
                        .then(customComparator.buildComparator(fieldAndDirection3[0], fieldAndDirection3[1] == "asc")))
            }
            else -> {
                return objects
            }
        }
    }
}

现在,在我的问题中,我还可以将这种模式也应用于我的实际服务中,但是让我们一次迈出这一步。

反射

@Suppress("UNCHECKED_CAST")
fun <T : Any> KClass<T>.compareByProperty(propName: String, asc: Boolean = true): Comparator<T> {
    val property = declaredMemberProperties.first { it.name == propName }
    val getter = property::get as (T) -> Comparable<*>

    if (asc) {
        return compareBy(getter)
    }
    return compareByDescending(getter)
}

第一个扩展函数compareByProperty使用反射按名称查找属性,然后提取属性的getter作为Conparable的选择器

然后取决于asc参数的getter被转化为Comparator的一般类型与标准功能的 :

帮手结合

inline fun <reified T : Any> Comparator<T>.thenByProperty(propName: String, asc: Boolean = true) =
    then(T::class.compareByProperty(propName, asc))

第二个扩展功能允许组合Comparator

用法

fun main() {
    val typeObjects = listOf(...)
    val comparator = TypeObject::class.compareByProperty("id")
        .thenByProperty("type", asc = false)

    val sortedTypes = typeObjects.sortedWith(comparator)
}

用法类似于本例中的主要功能。 通过::访问KClass对象。 然后调用第一个扩展函数,并使用第二个扩展函数组合Comparable

  相关解决方案