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

时间:2019-02-13 19:01:52

标签: java generics kotlin

我有DTO对象,然后我需要对对象集合进行动态排序,有点像数据库ORDER BY,但是我没有数据库来实际使查询完成实际工作(我知道,但是我今天有什么...)。

在我看来,StatusComparator和TypeComparator应该可以完全通用地重写,并且还可以利用反射,这样我就不需要为API中的每个对象编写一个(我还有三个)这项服务,而且确实变得越来越重复)。

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

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

DTO:

$(document).ready(function() {
    $('.model_link').click(function() {
        var buttonName = $(this).attr('id').split('_')[0];
        $('#button').val(buttonName);
        $('form').submit();
    });
});

比较:

@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
)

我的类型服务中的示例用法:

@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
            }
        }
    }
}

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

1 个答案:

答案 0 :(得分:1)

反射

@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使用反射按名称查找属性,然后提取属性的吸气剂作为Conparable的选择器

然后根据asc参数,使用标准函数compareBy and compareByDescendinggetter转换为通用类型的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