英文:
Golang -- Avoiding duplicate code with unknown type
问题
还是一个新手。作为一个简单的项目,我正在尝试为Kiva的API编写一个API包装器。我试图避免编写处理API的分页响应的重复代码,但是由于Go语言缺乏泛型类型,我还没有找到一种方法来实现,并且不确定是否可能。
我尝试使用类型切换和反射包来避免在多个函数中执行完全相同的操作,但是没有成功。这是我当前的(完全不可用的)代码版本:
type PagingData struct {
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Pages int `json:"pages"`
}
type PagedLoanResponse struct {
Paging PagingData `json:"paging"`
Items []Loan `json:"loans"`
}
type PagedLenderResponse struct {
Paging PagingData `json:"paging"`
Items []Lender `json:"lenders"`
}
func (c *Client) doPaged(method string, urlpath string, query url.Values, body io.Reader, v interface{}, numPages int) ([]interface{}, error) {
// 省略部分代码...
}
func (c *Client) GetNewestLoans(numPages int) ([]Loan, error) {
// 省略部分代码...
}
希望这可以帮助你。如果你有任何其他问题,请随时问我。
英文:
Still a bit of a go newb. As a simple project, I'm trying to write an API wrapper for Kiva's API. I'm trying to avoid writing duplicate code for handling paged responses from the API, but, given Go's lack of generic types, I haven't found a way to do it and am not sure if it's possible.
I've tried using type switches and the reflect package to avoid doing the exact same thing in multiple functions but haven't had any luck. This is the current iteration of my (totally non-functional) code:
type PagingData struct {
Total int `json: "total"`
Page int `json: "page"`
PageSize int `json: "page_size"`
Pages int `json: "pages"`
}
type PagedLoanResponse struct {
Paging PagingData `json: "paging"`
Items []Loan `json: "loans"`
}
type PagedLenderResponse struct {
Paging PagingData `json: "paging"`
Items []Lender `json: "lenders"`
}
func (c *Client) doPaged(method string, urlpath string, query url.Values, body io.Reader, v interface{}, numPages int) ([]interface{}, error) {
if numPages < 0 {
return nil, fmt.Errorf("less than zero is unacceptable")
}
pr := reflect.New(reflect.TypeOf(v))
if query == nil {
query = url.Values{}
}
// get the first page
err := c.do(method, urlpath, query, body, &pr)
if err != nil {
return nil, err
}
if pr.Paging.Pages == 1 {
return pr.Items, nil
}
if numPages == 0 {
numPages = pr.Paging.Pages
}
items := make(reflect.New(reflect.TypeOf(pr.Items)), 0, pr.Paging.Total)
items = append(items, pr.Items...)
for i := 2; i <= numPages; i++ {
query.Set("page", strconv.Itoa(i))
err := c.do("GET", urlpath, query, nil, &pr)
if err != nil {
return nil
}
items = append(items, pr.Items...)
}
return items, nil
}
func (c *Client) GetNewestLoans(numPages int) ([]Loan, error) {
baseURL := "/v1/loans/newest"
var p PagedLoanResponse
loans, nil := c.doPaged("GET", baseURL, nil, nil, p, numPages)
}
答案1
得分: 1
你可以做的一件事是为所有可分页的内容创建一个接口,可以称之为Pagable
:
type Pagable interface {
Paging() PagingData
}
然后创建一个实现该接口的单个类型:
type Pager struct {
Paging PagingData `json:"paging"`
}
func (p Pager) Paging() PagingData {
return p.Paging
}
接下来,你可以在其他类型中"嵌入"一个Pager
,以自动实现Pagable
接口并包含这些字段:
type PagedLenderResponse struct {
Paging
Items []Lender `json:"lenders"`
}
如果doPaged
函数接受一个Pagable
而不是interface{}
作为v
参数,你可以在它上调用Paging()
方法,获取分页信息,然后json
包可以将其余部分写入响应中。不需要使用interface{}
和反射。
英文:
One thing you could do is have an interface for anything that is pagable. Perhaps called Pagable
:
type Pagable interface { Paging() PagingData }
and a single type that implements it:
type Pager struct {
Paging PagingData `json:"paging"`
}
func(p Pager) Paging() PagingData {
return p.Paging
}
Then your other types can "embed" a Pager
to automatically implement Pagable
and include those fields:
type PagedLenderResponse struct {
Paging
Items []Lender `json: "lenders"`
}
If doPaged
then accepts a Pagable
instead of interface{}
for v
, you can call Paging()
on it, and get the page stuff, and then the json package can write the rest of it out to the response for you. No interface{}
and no reflect needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论