通常来说,我们会使用某个具体的对象(struct)来作为receiver实现接口,但有时候,使用函数作为receiver可以起到不一样的效果。

使用函数作为receiver一个最常见的例子是HandleFunc:

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

这里将HandleFunc作为接收器,实现了ServeHTTP方法,同时也实现了http.Handler接口

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

代码注释中对与HandleFunc的解释如下:

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.

HandleFunc的定位是:适配器。为什么这么说?有了这个适配器,我们就可以这样完成一个http server:

func main() {
    http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
        // do something
    })

    http.ListenAndServe(":8888", nil)
}

这里我们简单的将一个函数作为实参传入了http.HandleFunchttp.HandleFunc底层调用了mux.Handle进行注册,mux.Handle函数签名如下:

func (mux *ServeMux) Handle(pattern string, handler Handler)

这里的Handler,就是上面提到的http.Handler接口。中间发生了什么,为什么一个函数突然变成了一个接口对象,从一个被定义的行为,变成了一个执行某种动作的对象。魔法发生在mux.HandleFunc

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler)) // 这里进行了类型的转化
}

经过一个类型转换,一个不具名函数类型,变成了一个具名函数类型HandlerFunc。然后在代码的调用链中,最终又调用了这个具名函数类型的ServerHTTP方法,这个ServerHTTP方法内部又是调用的这个函数本身。

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	// ...

	handler.ServeHTTP(rw, req) // 这里的handle就是我们传入的不具名函数对象转化而来的具名函数对象
}

那么,如果没有HandleFunc适配器,会发生什么?

func (mux *ServeMux) Handle(pattern string, handler Handler)

这个函数的第二个形参是一个接口,那么我们需要定义一个接口的具体实现,接口的具体实现需要一个receiver,我们需要定义一个receiver,,一般来说,我们可能会定义一个空struct,并让他来实现ServeHTTP(ResponseWriter, *Request)方法(成为Handler接口对象)

type HandlerReceiver struct {
}

func (h HandlerReceiver) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// ...
}

这个我们实现一个接口handler的过程,如果有多个handler呢,一个应用程序,上百个接口总是有的,难道我们需要定义上百个甚至更多空的、无意义的struct来完成这个适配?

Function As Method Receiver还能做什么?TODO