问题描述
我有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
}
}
}
}
现在,在我的问题中,我还可以将这种模式也应用于我的实际服务中,但是让我们一次迈出这一步。
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
使用反射按名称查找属性,然后提取属性的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
: