Golang泛型的应用:约束传入对象拥有的方法
问题
在开发后台应用时,有比较多重复的查询数据库的请求,我数据库查询是基于gorm gen
实现的,所有的查询请求中都包含page、limit、order、desc,也就是分页查询的参数,对于每一个请求都根据查询表的不同来构造查询语句实在是太麻烦,太冗长了,所有想到了实现一个通用的根据分页参数构造查询的函数,但是遇到了问题,gen
的查询语句构造是链式调用,函数返回的对象都是不一样的interface
,导致没有办法直接使用。
type QueryBaseReq struct {
Page int `json:"page" validate:"required"`
Limit int `json:"limit" validate:"required"`
Order string `json:"order"`
Desc bool `json:"desc"`
}
解决方案
使用golang的泛型,约束传入的对象拥有哪些方法,并且这些方法返回的值就是这个类型本身,在我们当前的这个需求当中,我们需要约束传入的查询对象拥有Limit、Order、Offset
方法,因此我们创建了一个interface
type IDo[T any] interface {
Limit(_ int) T
Order(_ ...field.Expr) T
Offset(_ int) T
}
该interface
拥有所需的函数且使用泛型T
,对T
没有任何要求。
创建查询函数DoQuery
// DoQuery 根据查询条件构造查询
func DoQuery[T IDo[T]](query QueryBaseReq, do T, model interface {GetFieldByName(fieldName string) (field.OrderExpr, bool)}) (T, error) {
order, ok := model.GetFieldByName(query.Order)
if ok {
if query.Desc {
do = do.Order(order.Desc())
} else {
do = do.Order(order.Asc())
}
} else {
return do, errors.New("排序字段不存在")
}
do = do.Limit(query.Limit).Offset(query.Page * query.Limit)
return do, nil
}
该查询函数使用泛型T
,对T的要求是满足interface IDo
约束实现的三个方法,而interface IDo
需要传入泛型约束,也还是T
,以实现链式调用。
第一个参数query
是查询参数,第二个参数do
是传入的查询语句构造对象,类型就是泛型T
,第三个参数model
,类型约束为拥有{GetFieldByName(fieldName string) (field.OrderExpr, bool)}
方法以获取排序Filed
。
这篇文章主要是记录如何在多个不同interface
但是拥有部分类似方法实现的情况下,仅约束函数参数实现这部分类似方法,实现通用方法。
type QueryBaseReq struct {
Page int `json:"page" validate:"required"`
Limit int `json:"limit" validate:"required"`
Order string `json:"order"`
Desc bool `json:"desc"`
}
type IDo[T any] interface {
Limit(_ int) T
Order(_ ...field.Expr) T
Offset(_ int) T
}
// DoQuery 根据查询条件构造查询
func DoQuery[T IDo[T]](query QueryBaseReq, do T, model interface {GetFieldByName(fieldName string) (field.OrderExpr, bool)}) (T, error) {
order, ok := model.GetFieldByName(query.Order)
if ok {
if query.Desc {
do = do.Order(order.Desc())
} else {
do = do.Order(order.Asc())
}
} else {
return do, errors.New("排序字段不存在")
}
do = do.Limit(query.Limit).Offset(query.Page * query.Limit)
return do, nil
}
许可协议:
GPL V3