Go语言使用泛型替换switch逻辑优化sqlx数据库查询代码问询
优化方案
你当前的双map方案已经解决了核心的冗余问题,还可以从以下几个方向做进一步优化,解决潜在问题、提升可维护性和类型安全性:
1. 合并元数据配置,避免双map不同步
当前两个独立map存在维护风险:新增查询类型时如果漏加某一个map的配置,会直接导致运行时错误。可以把SQL语句和构造函数合并为同一个元数据结构体,只用一个map维护:
// 定义查询元数据结构体,统一管理每个查询类型的关联配置 type QueryMeta struct { SQL string NewFunc func() interface{} // 注意这里返回结构体指针,避免后续扫描错误 } var queryMetas map[int]QueryMeta
新增类型时只需要加一条配置,不会出现两边不一致的问题。
2. 修复原有代码的隐藏扫描错误
你当前构造函数返回的是值类型结构体,放到interface{}后取地址&r拿到的是*interface{}类型,不是结构体指针,sqlx的StructScan会报错。构造函数需要调整为返回结构体指针:
// 正确写法 scanStructs[progressbar] = func() interface{} { return &ProgressBar{} } // 后续扫描直接传r即可,不需要再取地址 if err := rows.StructScan(r); err != nil { // 错误处理 }
3. 用iota规范枚举定义
Go虽然没有原生枚举,但可以用iota更优雅地定义常量,避免手动赋值出错:
const ( progressbar = iota + 1 textbox calendar )
新增类型时直接在后面加常量名即可,不需要手动维护数字顺序。
4. 泛型优化返回值(Go 1.18+支持)
如果你的项目可以用Go 1.18及以上版本,泛型可以彻底解决返回[]interface{}需要类型断言的问题,实现类型安全:
// 通用查询函数,返回对应类型的切片,无需断言 func PerformSqlQuery[T any](sql string) ([]T, error) { rows, err := db.Queryx(sql) if err != nil { return nil, fmt.Errorf("db.Queryx() failed: %v", err) } defer rows.Close() var result []T for rows.Next() { var t T if err := rows.StructScan(&t); err != nil { return nil, fmt.Errorf("rows.StructScan() failed: %v", err) } result = append(result, t) } return result, nil } // 调用示例 bars, err := PerformSqlQuery[ProgressBar]("SELECT * FROM progress_bars")
如果需要保留原有按类型编号查询的逻辑,也可以把泛型和元数据map结合,对外暴露按类型编号查询的接口,内部做类型转换即可。
5. 扩展建议
如果后续查询逻辑变复杂(比如需要传查询参数、分页等),可以把QueryMeta扩展为接口,不同查询类型实现各自的接口方法,扩展性更强。
内容的提问来源于stack exchange,提问作者AlwaysLearningNewStuff
相关产品推荐
相关产品推荐

