Go 返回具有数据成员的多态类型

Go 返回具有数据成员的多态类型,go,polymorphism,Go,Polymorphism,我试图编写一个函数getTargetServer来返回一个多态类型,该多态类型既有一个数据成员URL,也有一个方法Close。这将是从返回的泛化,但我希望能够返回一个自定义类型,Close是NOP type externalTestServer struct { URL string } func (externalTestServer) Close() {} func getTargetServer() *externalTestServer { if urlbase, o

我试图编写一个函数getTargetServer来返回一个多态类型,该多态类型既有一个数据成员URL,也有一个方法Close。这将是从返回的泛化,但我希望能够返回一个自定义类型,Close是NOP

type externalTestServer struct {
    URL string
}

func (externalTestServer) Close() {}

func getTargetServer() *externalTestServer {
    if urlbase, ok := optionals["urlbase"].(string); ok {
        return &externalTestServer{URL: urlbase}
    } else {
        testServer := httptest.NewServer(newMyServerHandler())
        // return testServer // ### Error ###
        return &externalTestServer{URL: testServer.URL}
    }
}

func Test_health_check(t *testing.T) {
    testServer := getTargetServer()
    defer testServer.Close()
    response, err := http.Get(testServer.URL + "/health")
    assert.NilError(t, err)
    assert.Assert(t, cmp.Equal(response.StatusCode, http.StatusOK))
}
这就像一个魅力,除了关闭总是一个不。当我取消注释指示的错误行以返回closable*服务器时,会收到以下错误消息:

无法在返回参数中使用testServer类型*httptest.Server作为类型*externalTestServer

我理解这个错误,但还没有发现一个解决方案,可以让我返回一个泛化*服务器的多态类型

注:Go之旅定义了如下接口类型:

接口类型定义为一组方法签名


因此,返回一个简单接口将不允许直接访问数据成员。

http.Server是一个结构,因此您不能返回概括该结构的多态对象。不过,您可以做其他事情:

type Server interface {
  GetURL() string
  Close() error
}

type testServer struct {
   URL string
}

func (t testServer) Close() error {}
func (t testServer) GetURL() string {return t.URL}

type httpServer struct {
    *http.Server
}

func (t httpServer) GetURL() string { return the url }

然后,您可以从函数返回服务器。

您可以创建一个结构,该结构包含一个字段,该字段是字符串URL,该字段是关闭函数。Close func可以由externalTestServer或httptest实现。服务器:


Chris Drew的方法非常适合这种情况,因为它需要最小的代码开销。换句话说,这是解决当今问题的最简单的办法。该解决方案使用隐式闭包的魔力,让接收方以多态方式引用Close的实现

我稍微简化的版本在这里

type genericServer struct {
    URL   string // snapshot of internal data
    Close func() // single-function polymorphism without 'interface'
}

func getTargetServer() genericServer {
    if urlbase, ok := optionals["urlbase"].(string); ok {
        return genericServer{URL: urlbase, Close: func() {}}
    }
    testServer := httptest.NewServer(newMyServerHandler())
    return genericServer{URL: testServer.URL, Close: testServer.Close}
}
通过将实际接口类型嵌入to return结构,可以无缝地扩展此概念,以更好地支持非平凡多态接口。在这种情况下,多态性是基于接口类型的内部使用而显式的。尽管如此,返回的类型是一个包含常量成员数据副本的包装器,因此其用法是相同的——有效地概括了此用例的用法

type Server interface {
    Close()
}

type nopServer struct {
}

func (nopServer) Close() {}

type genericServer struct {
    URL string // snapshot of specific data
    Server // embedded interface type for explicit polymorphism
}

func getTargetServer() genericServer {
    if urlbase, ok := optionals["urlbase"].(string); ok {
        return genericServer{URL: urlbase, Server: nopServer{}}
    }
    testServer := httptest.NewServer(newMyServerHandler())
    return genericServer{URL: testServer.URL, Server: testServer}
}

请注意,URL值不是实现的活动成员,因此这些解决方案仅在数据值不会更改时才有意义,尽管使用指针可能会克服这一限制。

Go中多态性的唯一形式是接口,接口只描述行为;他们不可能有字段。-2:以这种速度,我永远不会变得好奇:回答得好。这与我最初认为必须采取的方法类似。虽然为东西创建额外的包装器有点笨重,但这不是我的偏好。这似乎完全符合我的期望-而且构造非常简单。甚至不需要externalTestServer-可以使用Close:func{}来创建NOP。
type genericServer struct {
    URL   string // snapshot of internal data
    Close func() // single-function polymorphism without 'interface'
}

func getTargetServer() genericServer {
    if urlbase, ok := optionals["urlbase"].(string); ok {
        return genericServer{URL: urlbase, Close: func() {}}
    }
    testServer := httptest.NewServer(newMyServerHandler())
    return genericServer{URL: testServer.URL, Close: testServer.Close}
}
type Server interface {
    Close()
}

type nopServer struct {
}

func (nopServer) Close() {}

type genericServer struct {
    URL string // snapshot of specific data
    Server // embedded interface type for explicit polymorphism
}

func getTargetServer() genericServer {
    if urlbase, ok := optionals["urlbase"].(string); ok {
        return genericServer{URL: urlbase, Server: nopServer{}}
    }
    testServer := httptest.NewServer(newMyServerHandler())
    return genericServer{URL: testServer.URL, Server: testServer}
}