为什么要在Go中的函数上实现接口?
我读到,在 Go 中,函数可以实现接口(就像在这个例子或这个例子中)。
但是让一个函数实现一个接口有什么价值呢?
例如,为什么要花时间定义一个接口、一个新的函数类型和一个方法,像这样:
type Handler interface {
ServeHTTP(*Conn, *Request)
}
type HandlerFunc func(*Conn, *Request)
func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) {
f(c, req)
}
相反,您可以只创建一个像这样的函数:
func ServeHTTP(f func(*Conn, *Request), c *Conn, req *Request) {
f(c, req)
}
提前致谢!
回答
单个函数(在您的示例中,func ServeHTTP, 不满足任何接口。
因此,将方法附加到函数类型的原因是为了满足接口。
这其中有什么价值?让任何其他类型实现接口的价值相同。让我们看看http.Handler界面,因为你提出了它。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
例如,您可以使用结构实现此接口:
type myHanlder struct {
/* some fields */
}
func (s *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
/* do things */
}
你也可以用一些更简单的类型来满足它。说,一个字符串:
type myStringHandler string
func (h myStringHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "The string is: %s", h)
}
但是如果您只想直接实现该功能,例如:
func myHandler(w http.ResponseWriter, r *http.Request) {
/* do things */
}
这不满足接口。所以我们需要创建一个匹配函数签名的类型,并提供接口方法。这就是http.HandlerFunc它的作用:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
如果问题是:为什么标准库的作者决定做ServerHTTP一个接口,而不是一个函数类型,而不用搜索旧的邮件列表档案来寻找线索,只有猜测是可能的,这在这里真的是题外话,但这里有一个小刺它:
- 接口更易于使用,因为它们可以轻松扩展/嵌入。也就是说,我可以实现一个既满足
http.Handler接口又满足其他接口的结构。如果http.Handler不是接口,。 - 这是惯用的。使用“鸭子打字”界面正好符合 Go 的做事方式。也许这有点同义反复,因为它适合 Go 的原因是因为它是这样做的。
- 如果您有一个复杂的处理程序,它可能会跟踪自己的状态,那么使用结构是自然的解决方案,并且将其公开为接口比闭包更自然(如果将其实现为函数类型,则需要这样做)。
作为一个思想实验,想象一下如果io.Reader接口代替函数类型:
type Reader func(p []byte) (n int, err error)
我认为很容易看出疯狂会随之而来。