Golang — Предотвращение дублирования кода с неизвестным типом

#go

#Вперед

Вопрос:

Все еще немного новичок в go. В качестве простого проекта я пытаюсь написать API-оболочку для API Kiva. Я пытаюсь избежать написания дублирующего кода для обработки выгружаемых ответов из API, но, учитывая отсутствие в Go универсальных типов, я не нашел способа сделать это и не уверен, возможно ли это.

Я пытался использовать переключатели типов и пакет reflect, чтобы избежать выполнения одного и того же в нескольких функциях, но безуспешно. Это текущая итерация моего (полностью нефункционального) кода:

 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, amp;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, amp;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:

Единственное, что вы могли бы сделать, это иметь интерфейс для всего, что доступно для просмотра. Возможно, вызывается Pagable :

 type Pagable interface { Paging() PagingData }
  

и единственный тип, который его реализует:

 type Pager struct { 
  Paging PagingData `json:"paging"`
}
func(p Pager) Paging() PagingData {
   return p.Paging
}
  

Тогда ваши другие типы могут «встраивать» a Pager для автоматической реализации Pagable и включать эти поля:

 type PagedLenderResponse struct {
    Paging
    Items  []Lender   `json: "lenders"`
}
  

Если doPaged затем принимает Pagable вместо interface{} for v , вы можете вызвать Paging() его и получить содержимое страницы, а затем пакет json может записать остальное в ответ для вас. Нет interface{} и отражение не требуется.

Комментарии:

1. Этот подход по-прежнему требует большего количества повторений, чем я надеялся, в каждой функции, вызывающей doPaged, чтобы удалить информацию о подкачке из ответов, но подход, который вы описываете, вероятно, настолько хорош, насколько я получу — спасибо!