Golang 推断接口

huangapple go评论91阅读模式
英文:

Golang inferred interfaces

问题

只是一个与语言设计相关的简单问题。
在像Swift这样的语言中,为了使一个结构体/类符合一个协议/接口,需要明确地声明它的符合关系,如下所示:

struct Dog: Animal {
    // 在这里实现Animal协议
}

但是为什么在Go语言中,没有明确的方式来显示一个结构体符合哪些接口呢?

这难道不会使事情变得不清晰,或者背后还有其他原因吗?

英文:

Just a simple language design related question here.
In languages like Swift, in order for a struct/class to conform to a protocol/interface, one needs to explicitly declare it as such

struct Dog: Animal {
    // implementation of Animal protocols here
}

But why is it that in Go, there are no explicit ways to show what interfaces a struct conforms to?

Isn't this just making things unclear or are there any other reasons behind it?

答案1

得分: 7

这允许演化以提供接口,即使对于从未显式声明遵守该接口的旧类型。

如果这些类型已经提供了正确的方法(即与您接口指定的API相同),它们将满足该接口。

这样可以在那些旧类型(它们不知道您的接口存在)和接受该接口实例的程序之间保持低耦合。

请参阅“Go接口的一些示例是什么?

Go的接口不是Java或C#接口的变体,它们更加强大。
它们是大规模编程和适应性、演化设计的关键。

“事实上,我不必事先花时间设计某种类型层次结构,然后在完成之前重新排列两三次。事实上,它易于正确实现,但更重要的是,我不必担心它,可以继续进行实际的算法。”

这些引用来自这篇2012年的文章,还有这个讨论串,您可以在其中阅读Rob Pike的评论

“在面向对象的语言中,我们可以将Circle、Square等子类定义为Shape的子类,但这是我们能够做出的唯一设计决策。
如果我们想要沿其他轴(可以这么说)对齐这些东西,比如拓扑层次或植物属,如果你是一个园艺师,你可能会迷失方向或陷入多重继承的困境。”

英文:

This allow to evolve to provide interface even for legacy types (which never explicitly declared respecting said interface)

If those types already provide the right methods (ie the same API as the one specified by your interface), they will satisfy that interface.

That allows to maintain a low coupling between those legacy types (which have no idea your interface exist) and your program, which accepts instances of that interface.

See "What are some examples of Go interfaces?"

> Go's interfaces aren't a variant on Java or C# interfaces, they're much more.
They are a key to large-scale programming and adaptable, evolutionary design.

> "It's the fact that I don't have to spend time up front designing some sort of type hierarchy and then rearranging it two or three times before I finish.
It's not even the fact that it's easy to do it right -
it's the fact that I just don't have to worry about it and can get on with the actual algorithm."

Those quotes are from this 2012 article, and also from this thread, where you can read Rob Pike's comment:

> "It's always bothered me in OO languages that we can make Circle, Square, etc. subclasses of Shape (say), but that's the one design decision we get to make.
What if we want to align those things along other axes (so to speak), like topological genus or plant genus, if you're a landscaper? You might lose your way or dig yourself into a hole with multiple inheritance."

答案2

得分: 3

扩展VonC的回答。

从问题中可以看出:

> 但是为什么在Go语言中,没有明确的方法来显示一个结构体符合哪些接口呢?

实际上有一些方法。参见Go FAQ: 如何确保我的类型满足一个接口?

假设我们有以下接口:

type Sayer interface {
	Say() string
}

我们想要创建一个实现该接口的新类型:

type MySayer struct{}

func (ms MySayer) Say() string {
	return "Foo"
}

在这种情况下,MySayer实现了Sayer接口。为了确保它确实实现了该接口,可以添加以下代码:

var _ Sayer = MySayer{}

如果你输入错误,比如将Say()方法命名为Sai(),它将在编译时报错。

然而,这只是一个编译时的“检查”,并没有说明MySayer实现了Sayer接口。为了达到这个目的,你可以添加一个“虚拟”的方法,方法的名称本身就说明了这个属性:

type Sayer interface {
	Say() string
	ImplementsSayer()
}

现在,任何类型要实现Sayer接口,都必须声明一个名为ImplementsSayer()的方法,一眼看过去就知道它实现了Sayer接口:

func (MySayer) ImplementsSayer() {}

你甚至不需要给接收者命名,因为它不会被使用。这行代码用简单的英语表达了:“MySayer实现了Sayer”。

由于这个方法是公开的(以大写字母开头),它也会出现在文档中,直接告诉你它实现了Sayer接口。但正如FAQ中所说:

> 大多数代码并不使用这样的约束,因为它们限制了接口的实用性。然而,有时候它们是必要的,以解决类似接口之间的歧义。

英文:

Extending VonC's answer.

From the question:

> But why is it that in Go, there are no explicit ways to show what interfaces a struct conforms to?

Actually there are some ways. See Go FAQ: How can I guarantee my type satisfies an interface?

Let's say we have the following interface:

type Sayer interface {
	Say() string
}

And we want to create a new type implementing it:

type MySayer struct{}

func (ms MySayer) Say() string {
	return "Foo"
}

In this case MySayer implements Sayer. To make sure it does, you can add the following line:

var _ Sayer = MySayer{}

Should you mistype something, e.g. name your Say() method Sai(), it will be a compile-time error.

Still, this is just a compile-time "check", and it does not document that MySayer implements Sayer. For this purpose you may add a "dummy" method whose name itself documents this property:

type Sayer interface {
	Say() string
	ImplementsSayer()
}

Now for any type to implement Sayer, they must declare a method named ImplementsSayer(), which judged by just a glimpse tells you it implements Sayer:

func (MySayer) ImplementsSayer() {}

You don't even need to name the receiver as it is not used. The line reads in plain English: "MySayer implements Sayer".

Since this method is exported (starts with a capital letter), it also appears in docs, telling in your face that it implements Sayer. But as the FAQ says:

> Most code doesn't make use of such constraints, since they limit the utility of the interface idea. Sometimes, though, they're necessary to resolve ambiguities among similar interfaces.

huangapple
  • 本文由 发表于 2017年1月3日 13:35:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/41437046.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定