使用Golang创建一个结构体并返回一个接口

4

这里是Go语言代码,函数newXXX返回一个接口,但为什么它不返回一个结构体呢?

type _ABitOfEverythingServer struct {
    v map[string]*examples.ABitOfEverything
    m sync.Mutex
}

type ABitOfEverythingServer interface {
    examples.ABitOfEverythingServiceServer  // interface
    examples.StreamServiceServer            // interface
}

func newABitOfEverythingServer() ABitOfEverythingServer { 
//<-why not return _ABitOfEverythingServer, is it a good way?
    return &_ABitOfEverythingServer{
        v: make(map[string]*examples.ABitOfEverything),
    }
}

3
关于人工代码片段的“为什么?”问题没有一个确定的答案。也许是因为技术原因,或者出于教育目的,或者是为了演示某些东西。或者只是一种品味问题。 - Volker
这只是一个猜测,但因为ABitOfEverythingServer是一个接口,如果你想要交换它,它会更加灵活,并且在测试中很容易进行“模拟”或“存根”。我建议阅读有关接口的内容。 - Popmedic
3个回答

10

我不知道以上代码片段中是否有特别的原因要返回接口。

但通常情况下,返回结构体是推荐的方法。还记得“接受接口,返回结构体”吗?

返回接口并没有简化模拟(mocking),因为客户端可以在需要模拟时定义接口(这正是 Golang 接口最美妙的地方)。

func newABitOfEverythingServer() *_ABitOfEverythingServer { // <- returning struct ptr
    return &_ABitOfEverythingServer{
        v: make(map[string]*examples.ABitOfEverything),
    }
}
对于上述重构版本(返回结构体),客户端可以简单地定义描述其需要什么的接口,并仅对其进行模拟测试。
type onlyStreamPart interface {
     examples.StreamServiceServer
}

// streamer does not care if the streamServer also
// implements examples.ABitOfEverythingServiceServer
// or not. (Think interface segregation from SOLID)
func streamer(stremServer onlyStreamPart) {
}

// which can be called easily as:
streamer(newABitOfEverythingServer())

这使得在测试streamer时,模拟变得更加简单,因为模拟实现不必实现examples.ABitOfEverythingServiceServer接口。

这是Java、C#等背景的开发人员常常误解的一个问题(这些语言中类型系统是基于名称而非基于结构的)。因为在那些语言中,客户端无法修改其所接受的接口,因为这需要在所有需要传递给客户端的类中添加implements newInterfaceDefinedByClient子句。


2

首先,您需要学习Go语言的基础知识。请参考 《Go语言之旅》

简化一个复杂的例子,

package main

import "fmt"

type S struct{ F int }

type I interface{}

func newI() I {
    return &S{F: 42}
}

func main() {
    i := newI()
    s := i.(*S)
    f := s.F
    fmt.Println(f)
}

游乐场:https://play.golang.org/p/tHbTZHEQZ-L

输出:

42
< p > newI 函数返回一个接口类型的值 I,其中包含一个具体类型为 *S 的值。


3
这个回答有贬低人的意味。 - Quesofat

0
如果您注意到,该结构体包含一个互斥锁。互斥锁不应该按值复制。作者希望抽象出实现细节,以便用户可以像使用其他数据类型一样使用它。因此,构造函数返回指向结构体的指针,并将其隐藏在导出接口后面。现在用户可以使用该数据类型,而不必担心它是否包含互斥锁。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接