如何基于泛型接口实现可动态检索的QueryBus?
QueryBus 实现优化方案
咱们先梳理下你的核心需求:注册一组和特定Query绑定的QueryHandler,通过Query找到对应的Handler并调用泛型的handle方法。你的思路方向是对的,但原代码存在类型不安全和键值不可靠的问题,咱们一步步调整优化。
原代码的核心问题
- 用
Query::class.toString()作为Map的键,不同环境下toString的输出可能不一致,不如直接用KClass作为键更可靠 - main函数中从Map取出Handler后,直接调用
handle(command)会因为类型擦除导致编译错误,编译器无法确认Handler的泛型参数和Query匹配
优化后的完整实现
1. 核心接口定义
首先调整接口,用KClass替代字符串作为标识,同时让QueryHandler和对应的Query类型强绑定:
interface Query<R> interface QueryHandler<R, Q : Query<R>> { fun handle(query: Q): R // 直接返回对应的Query的KClass,替代字符串 fun listenTo(): KClass<Q> }
2. DTO和具体Query/Handler实现
这部分逻辑可以保留,只需要修改listenTo方法的返回值:
// DTOs data class BookDto(val name: String) // List books query data class ListBooksQuery(val page: Int = 1) : Query<List<BookDto>> class ListBooksQueryHandler : QueryHandler<List<BookDto>, ListBooksQuery> { override fun handle(query: ListBooksQuery): List<BookDto> { return listOf(BookDto("Dune"), BookDto("Dune II")) } override fun listenTo(): KClass<ListBooksQuery> = ListBooksQuery::class } // Get book query data class GetBookQuery(val name: String) : Query<BookDto?> class GetBookQueryHandler : QueryHandler<BookDto?, GetBookQuery> { override fun handle(query: GetBookQuery): BookDto? { // 这里修正原代码的返回值类型,和Query定义的BookDto?匹配 return if (query.name == "Dune") BookDto("Dune") else null } override fun listenTo(): KClass<GetBookQuery> = GetBookQuery::class }
3. 类型安全的QueryBus实现
咱们封装一个QueryBus类,用泛型方法来确保类型安全,避免直接操作Map时的类型问题:
class QueryBus { private val handlers = mutableMapOf<KClass<out Query<*>>, QueryHandler<*, *>>() // 注册Handler的方法 fun <R, Q : Query<R>> register(handler: QueryHandler<R, Q>) { handlers[handler.listenTo()] = handler } // 执行Query的核心方法,用泛型确保类型匹配 @Suppress("UNCHECKED_CAST") fun <R, Q : Query<R>> execute(query: Q): R { val handler = handlers[query::class] as? QueryHandler<R, Q> ?: throw IllegalArgumentException("No handler registered for query ${query::class.simpleName}") return handler.handle(query) } }
4. 运行测试
现在main函数可以安全且简洁地调用了:
fun main(args: Array<String>) { // 初始化QueryBus并注册Handler val queryBus = QueryBus().apply { register(ListBooksQueryHandler()) register(GetBookQueryHandler()) } // 测试ListBooksQuery val listResult = queryBus.execute(ListBooksQuery()) println("List books result: $listResult") // 输出 [BookDto(name=Dune), BookDto(name=Dune II)] // 测试GetBookQuery val getResult = queryBus.execute(GetBookQuery("Dune")) println("Get book result: $getResult") // 输出 BookDto(name=Dune) val getNotFoundResult = queryBus.execute(GetBookQuery("Unknown")) println("Get not found book result: $getNotFoundResult") // 输出 null }
优化点说明
- 类型安全:通过泛型方法
execute确保传入的Query和对应的Handler返回类型完全匹配,编译期就能发现类型不匹配的问题 - 可靠的键值:用
KClass作为Map的键,避免字符串toString的不确定性 - 封装性:把Handler的注册和Query的执行逻辑封装在QueryBus类中,对外暴露简洁的API,避免直接操作底层Map
内容的提问来源于stack exchange,提问作者Valentin Trinqué
相关产品推荐
相关产品推荐

