在../src/io/io.go
,下面的界面有Write
方法
type Writer interface {
Write(p []byte) (n int, err error)
}
在../src/net/http/server.go
,接口ResponseWriter
下面实现了Write
接口的Writer
方法
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
从编码风格方面来看,Writer
界面是否嵌入ResponseWriter
界面首选实施呢?
我觉得你在接触embedding。
所以在上面的例子中,可以像这样重写接口定义:
type ResponseWriter interface {
Header() Header
io.Writer // embedded interface type
WriteHeader(statusCode int)
}
但这是与io
包的预先安排。
http/server.go
接口定义能够在不知道io
包的情况下存在。
通过http/server.go
的界面需要一个功能Write
与io.Writer
具有相同的签名,这允许许多其他好处,而无需建立与(外部)io
包的正式链接。
请记住,界面本身根本不执行任何操作。它简单地说,“如果您的软件包使用这些签名实现所有这些功能,那么它可以在需要X接口的地方使用”。
可以把它想象成一种契约类型:“如果我说在这里可以使用FooInterface
,那么做任何我所说的FooInterface
所做的事情都是可以接受的。”
根据定义,Writer
需要以某种方式实现Write
。从界面的角度来看,该特定方法的作用并不重要,只有它存在,并且接受byte
数组,返回int
和error
。它的作用取决于包的实现。
也许它编程一架无人机来编写字节,也许它会破坏时代广场的广告牌并在那里打印消息。重要的是,至少,您可以调用Write
并获得预期的返回值。副作用不是由接口定义的,而是由包中的实现定义的。
具有正确签名的Write
函数存在的事实使得包符合Writer
接口。它可能有十几种其他方法;只有Write
符合要求才有意义。
ResponseWriter
和Writer
只是因为ResponseWriter
说“我碰巧要求ResponseWriter
s实现与Writer
相同的接口 - 我们都实现Write([]byte) (int, error)
;要成为ResponseWriter
,它也必须实现Header
和WriteHeader
。”如果Skywriting
包也实现了这两个函数,那么它将是一个ResponseWriter
。
type ResponseWriter interface
没有定义Header
和WriteHeader
做什么,只是他们必须存在并拥有正确的签名。与Writer
的关系只是我们已经同意概念上ResponseWriter
是一个专门的Writer
。完全可以定义一个不包含OddResponseWriter
的Write
接口(虽然它会令人困惑)。
更专业的名称意味着具有更专业化名称的接口是具有更简单名称的接口的超集,但是Go本身没有强制执行此接口。正如@colminator所说,我们可以通过嵌入更简单的类型来强制执行此操作;这使得编译器可以强制专用接口也满足不太专业化的接口契约,并确保如果嵌入式接口的需求发生变化但嵌入器没有改变以满足它,代码将无法编译。
这种接口实现为我们提供了很大的灵活性,可以将一个包替换为另一个包而无需重写。如果一个函数说“请传递给我一个Writer
界面,这样我就可以写出我的结果”,如果你用一个不同的名字交换一个新的包,那么不需要触摸该代码,只要你输入的内容有一个匹配的Writer
。