Проблема с удовлетворением интерфейсу в Go

Нижеприведенный код не удовлетворяет созданному для него интерфейсу (думаю, всем понятно для чего интерфейс - DIP, модули верхнего уровня не должны зависеть от деталей и т.д.).

Ошибка при компиляции.

.\http_client_interface.go:118:13: cannot use NewClient() (type *Client) as type HTTPClient in assignment:
        *Client does not implement HTTPClient (wrong type for Classic method)
                have Classic() *Classic
                want Classic() Requester

В упор не понимаю почему он не удовлетворяет этому интерфейсу.

Упрощенный код (именно поэтому в одном пакете - в реальности, конечно, нет), который можно запустить.

package main

import (
    "bytes"
    "fmt"
    "io"
    "time"
    "net/http"
    "net/url"

)


func NewClient(timeouts ...time.Duration) *Client {
    timeout := 120 * time.Second
    if len(timeouts) > 0 {
        timeout = timeouts[0]
    }

    return &Client{
        classic: NewClassic(timeout),
        Timeout: timeout,
    }
}

func NewClassic(timeouts ...time.Duration) *Classic {
    timeout := 120 * time.Second
    if len(timeouts) > 0 {
        timeout = timeouts[0]
    }

    return &Classic{
        Client: &http.Client{Timeout: timeout},
    }
}


type Client struct {
    classic *Classic
    Timeout time.Duration
}


func (client *Client) SetTimeout(timeout time.Duration) {
    client.Timeout = timeout
}


type Classic struct {
    *http.Client
}

func (client *Client) Classic() *Classic {
    return client.classic
}


func (client *Classic) SetTimeout(timeout time.Duration) {
    client.Client.Timeout = timeout
}

func (client *Classic) Get(
    urlPath string, queryValues url.Values, headerValues http.Header,
    cookies ...*http.Cookie) *Response {
    // тут много кода.... который не нужен для демонстрации
    return &Response{}
      
}


type Response struct {
    status int
    err    error
    body   []byte
}

func (r *Response) Body() []byte {

    if r.body == nil {
        return []byte{}
    }

    return r.body
}

func (r *Response) Reader() io.ReadCloser {
    return io.NopCloser(bytes.NewReader(r.body))
}

func (r *Response) Error() error {
    return r.err
}

func (r *Response) Status() int {
    return r.status
}

//-----------------------------------------------
// интерфейс
//-----------------------------------------------
type HTTPClient interface {
    SetTimeout(time.Duration)
    Classic() Requester
}
 
type Responser interface {
    Body() []byte
    Reader() io.ReadCloser
    Error() error
    Status() int
}


type Requester interface {
    SetTimeout(time.Duration)
    
    Get(urlPath string,
        query url.Values,
        headers http.Header,
        cookies ...*http.Cookie) Responser
}        



func main(){
     var client HTTPClient
     client =  NewClient()
     
     fmt.Printf("%v\n", client.Classic())
    
}


Ответы (1 шт):

Автор решения: Pak Uula

Поменяйте сигнатуры двум методам:

  • func (client *Classic) Get(...) пусть возвращает не конкретный метод Response, а интерфейс Responser
  • func (client *Client) Classic() пусть возвращает интерфейс Requester

После такой замены приведённая вами тестовая программа компилируется в go 1.16

package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "time"
)

func NewClient(timeouts ...time.Duration) *Client {
    timeout := 120 * time.Second
    if len(timeouts) > 0 {
        timeout = timeouts[0]
    }

    return &Client{
        classic: NewClassic(timeout),
        Timeout: timeout,
    }
}

func NewClassic(timeouts ...time.Duration) *Classic {
    timeout := 120 * time.Second
    if len(timeouts) > 0 {
        timeout = timeouts[0]
    }

    return &Classic{
        Client: &http.Client{Timeout: timeout},
    }
}

type Client struct {
    classic *Classic
    Timeout time.Duration
}

func (client *Client) SetTimeout(timeout time.Duration) {
    client.Timeout = timeout
}

type Classic struct {
    *http.Client
}

func (client *Client) Classic() Requester {
    return client.classic
}

func (client *Classic) SetTimeout(timeout time.Duration) {
    client.Client.Timeout = timeout
}

func (client *Classic) Get(
    urlPath string, queryValues url.Values, headerValues http.Header,
    cookies ...*http.Cookie) Responser {
    // тут много кода.... который не нужен для демонстрации
    return &Response{}

}

type Response struct {
    status int
    err    error
    body   []byte
}

func (r *Response) Body() []byte {

    if r.body == nil {
        return []byte{}
    }

    return r.body
}

func (r *Response) Reader() io.ReadCloser {
    return io.NopCloser(bytes.NewReader(r.body))
}

func (r *Response) Error() error {
    return r.err
}

func (r *Response) Status() int {
    return r.status
}

//-----------------------------------------------
// интерфейс
//-----------------------------------------------
type HTTPClient interface {
    SetTimeout(time.Duration)
    Classic() Requester
}

type Responser interface {
    Body() []byte
    Reader() io.ReadCloser
    Error() error
    Status() int
}

type Requester interface {
    SetTimeout(time.Duration)

    Get(urlPath string,
        query url.Values,
        headers http.Header,
        cookies ...*http.Cookie) Responser
}

func main() {
    var client HTTPClient
    client = NewClient()

    fmt.Printf("%v\n", client.Classic())

}
→ Ссылка