Go项目中的接口组织

nwlqm0z1  于 2023-05-27  发布在  Go
关注(0)|答案(2)|浏览(244)

我不太明白Go wiki的这句话:“Go语言的接口通常属于使用接口类型的值的包,而不是实现这些值的包”,接口在使用的地方声明是正确的吗?
如果我有这个test.go文件

package test

type Tester struct {...}

func NewTester(){
     return Tester{...}
}

func(t * Tester) GetCache(key string) (value string, error){...}
func(t * Tester) GetDB(key string) (value string, error){...}

现在,假设我有20个包,其中我使用GetCache方法。我是否需要在20个包中的每个包中定义接口,如下所示:

type TestCache interface {
   GetCache(key string) (value string, error)
}

它真的可以维护吗?如果GetCache更改了接口协定会怎么样?我需要更新包中的所有20个声明。在定义方法的包中声明每个接口不是更好吗?

yjghlzjz

yjghlzjz1#

是的,你的陈述是正确的--你 * 可能 * 想把你的接口存储在实际依赖它们的对象附近,并且由于Golang接口的implicit性质,它是可以实现的。来自典型OOP语言背景的工程师(比如我来自C#)通常很难理解这种语言特性的目的是什么。我可以详细说明为什么你描述的结构是有意义的。假设你的 library 中有一个这样的实体:

type Lib struct {
  Foo(){}
  Foo2(){}
  Foo3(){}
  Foo4(){}
}

type LibInterface interface {
  Foo()
  Foo2()
  Foo3()
  Foo4()
}

如果你在同一个包中定义了一个接口,那么所有的用户都将注入Lib暴露的所有4个方法,即使有些用户只需要1个或2个。所以你明确地说:“嘿,我需要那个库中的这4个方法”,这是不正确的,因为你只使用了1个。现在为它编写单元测试:

type LibUser struct{
   lib LibInterface
}
func New(lib LibInterface) *LibUser {
   lib = lib
}

你需要为你的依赖定义一个mock:

type LibMock struct{}
func (LibMock) Foo(){}
func (LibMock) Foo1(){}
func (LibMock) Foo2(){}
func (LibMock) Foo3(){}
func (LibMock) Foo4(){}

因此,您为接口公开的所有4个方法定义了一个mock,即使您只使用了1个。接下来会发生什么?Lib引入了一种新的方法--你可以访问所有的lib消费者并更新模拟,即使你仍然不使用它们!是不是很烦人?当然,您可以使用像Mockery这样的工具,将mock与接口和实现保持在一起,并在所有测试中提取整个blob。但是...干净吗Go使您能够以更清晰的方式实现它。当LibUser为其依赖项定义自己的接口时,它使用的是非常明确的。此外,如果你来自Java或C#背景,你会记得当一个库没有抽象时是多么烦人,所以你不能模拟它们,你只需要生活在没有测试或创建自己的 Package 器的情况下!所以这个问题在Go中不存在。在我看来,由消费者声明接口听起来是一种干净的方法。但同样-它不是一个规则,你可以决定你自己想如何结构你的项目。
这些是我在构建了大量用Go编写的服务和库之后的个人思考,这些服务和库来自OOP背景,其中接口是显式的

5vf7fwbs

5vf7fwbs2#

您可以使用类似

var _ TestCache = &Tester{}

在其他包。如果更改Tester实现,程序将不再编译
参见https://go.dev/play/p/L4bQYBcr4jv
包不依赖于具体的实现,而是依赖于同一个包定义的接口。多亏了接口鸭子打字。
是的,你可以为接口定义另一个包,在那里定义接口,并依赖于它。但在这种情况下耦合度会更高。

  • 此外,接口命名约定要求Tester应为接口名称,而不是struct https://go.dev/doc/effective_go#interface-names

相关问题