You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于泛型接口实现可动态检索的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é

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:26:03