Golang — 避免使用未知类型的重复代码

huangapple go评论81阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2016年10月22日 06:45:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/40186434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定