Go语言分页列表API全结果获取的抽象实现方案请求
Go 分页列表全结果通用获取实现
前提说明
在Go语言中实现分页列表API的全结果获取归类时遇到困难,寻求通用实现方案。本文为多平台发布。
目标需求
现有如下分页列表获取方法及请求/响应结构,需实现一个通用组件,循环调用该方法直至获取所有结果:
// request 请求对象 type request struct { nextToken *string pageSize int } // response 响应对象 type response struct { nextToken *string rows []interface{} } // MAX 分页列表的最大值 var MAX = 20 // ListSomething 分页列表获取示例方法 // 根据指定的pageSize输出最多20个数字序列(0~) func ListSomething(request request) response { var rows []interface{} var nextToken *string offset, _ := strconv.Atoi(*request.nextToken) limit := offset + request.pageSize if limit > MAX { limit = MAX nextToken = nil } else { limitStr := strconv.Itoa(limit) nextToken = &limitStr } for i := offset; i < limit; i++ { rows = append(rows, i) } return response{ rows: rows, nextToken: nextToken, } }
需支持类似ListSomethingA、ListSomethingB这类具有共性的分页方法:
- 请求对象均包含
nextToken和pageSize字段 - 响应对象均包含
nextToken和rows字段 - 调用形式需简洁,例如:
fmt.Println(NewLooper(fetchSomething).Loop()) // 输出: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
问题现状
已有初步的Looper结构,但无法抽象不同的分页方法、请求及响应对象:
// Looper 分页列表获取类 type Looper struct { // 抽象后的方法和请求/响应对象 } // NewLooper Looper构造函数 func NewLooper(/* 抽象后的方法或请求/响应对象 */) *Looper{ return &Looper{ // 抽象后的方法和请求/响应对象 } } // Loop 循环调用直到response.nextToken == nil并返回所有结果 func (l *Looper) Loop() []interface{} { firstToken := "0" nextToken := &firstToken var ans []interface{} for { response := 抽象方法(/* 抽象请求 */) ans = append(ans, response.rows...) if response.nextToken == nil { break } nextToken = response.nextToken } return ans }
解决方案
利用Go的接口特性抽象分页请求与响应的共性,定义通用的Looper组件:
1. 定义通用接口
import "strconv" // PaginatorRequest 分页请求通用接口,所有分页请求需实现此接口 type PaginatorRequest interface { SetNextToken(token *string) // 设置下一页标识 SetPageSize(size int) // 设置每页大小 } // PaginatorResponse 分页响应通用接口,所有分页响应需实现此接口 type PaginatorResponse interface { GetNextToken() *string // 获取下一页标识 GetRows() []interface{} // 获取当前页数据行 }
2. 让现有请求/响应实现接口
为原有的request和response类型添加接口实现:
// 给request类型实现PaginatorRequest接口 func (r *request) SetNextToken(token *string) { r.nextToken = token } func (r *request) SetPageSize(size int) { r.pageSize = size } // 给response类型实现PaginatorResponse接口 func (r response) GetNextToken() *string { return r.nextToken } func (r response) GetRows() []interface{} { return r.rows }
3. 定义通用Looper组件
// Fetcher 分页数据获取函数类型,接收分页请求,返回分页响应 type Fetcher func(req PaginatorRequest) PaginatorResponse // Looper 通用分页全量获取组件 type Looper struct { fetcher Fetcher // 分页数据获取函数 pageSize int // 默认每页大小 initToken *string // 初始请求标识 } // Option Looper配置选项 type Option func(*Looper) // WithPageSize 设置每页大小 func WithPageSize(size int) Option { return func(l *Looper) { l.pageSize = size } } // WithInitToken 设置初始请求标识 func WithInitToken(token *string) Option { return func(l *Looper) { l.initToken = token } } // NewLooper 创建Looper实例 func NewLooper(fetcher Fetcher, opts ...Option) *Looper { // 默认配置 looper := &Looper{ fetcher: fetcher, pageSize: 10, // 默认每页10条 initToken: strPtr("0"), // 默认初始标识为"0" } // 应用自定义配置 for _, opt := range opts { opt(looper) } return looper } // strPtr 辅助函数:将字符串转为指针 func strPtr(s string) *string { return &s } // Loop 循环获取所有分页数据 func (l *Looper) Loop() []interface{} { var allRows []interface{} currentToken := l.initToken for { // 创建请求实例(需根据实际请求类型调整,此处以原request为例) req := &request{} req.SetNextToken(currentToken) req.SetPageSize(l.pageSize) // 调用分页获取函数 resp := l.fetcher(req) allRows = append(allRows, resp.GetRows()...) // 检查是否还有下一页 currentToken = resp.GetNextToken() if currentToken == nil { break } } return allRows }
4. 适配原有分页方法
将ListSomething适配为符合Fetcher类型的函数:
func fetchSomething(req PaginatorRequest) PaginatorResponse { // 类型断言转换为具体请求类型 reqImpl, ok := req.(*request) if !ok { panic("invalid request type for ListSomething") } return ListSomething(*reqImpl) }
5. 调用示例
func main() { // 使用默认配置创建Looper looper1 := NewLooper(fetchSomething) fmt.Println(looper1.Loop()) // 输出: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] // 自定义每页大小为5 looper2 := NewLooper(fetchSomething, WithPageSize(5)) fmt.Println(looper2.Loop()) // 输出: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] }
扩展支持其他分页方法
对于ListSomethingA这类其他分页方法,只需:
- 让其
requestA实现PaginatorRequest接口 - 让其
responseA实现PaginatorResponse接口 - 编写对应的适配函数,即可复用Looper组件:
func fetchSomethingA(req PaginatorRequest) PaginatorResponse { reqImpl, ok := req.(*requestA) if !ok { panic("invalid request type for ListSomethingA") } return ListSomethingA(*reqImpl) } // 调用 looperA := NewLooper(fetchSomethingA, WithPageSize(20)) fmt.Println(looperA.Loop())
内容的提问来源于stack exchange,提问作者ponyo877
相关产品推荐
相关产品推荐

