概要
-
Go Design Patterns の中で、Go の標準ライブラリで Adapter Pattren が使用されている例として
http.HandleFunc関数について触れられていた。そこで理解したことをここに書く。(io.Pipe関数についても触れられているが、これについては次回書く。) - Adapter Pattern は、本来は異なるインタフェースを目的のインタフェースとして使用できるように変換する方法。Adapter が変換のための部品。
- http.HandleFunc関数は、任意の関数を http.Handlerインターフェースが持つ ServeHTTP メソッドの実装として変換する Adapter。
- ↑と思ったが、本当はhttp.HandlerFunc型が Adapterとしての核?
Adapter としての http.HandleFunc
- httpサーバのルートパスと関数の登録において、http.Handlerインタフェースを実装する書き方とhttp.HandleFunc関数に自分で実装した関数を渡す書き方がある。
- http.HandleFunc関数が Adapterに相当する。
http.Handlerインタフェースを実装する書き方
- http.Handleメソッドにルートパスと http.Handlerインタフェースを渡す。
- http.Handlerインタフェースを満たす型を実装し、ServeHTTPメソッドに目的の処理を実装する。
- 下記のソースコードは、Go Designe Patterns より引用。
type MyServer struct {
Msg string
}
func (m *MyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, m.Msg)
}
func main() {
server := &MyServer{
Msg: "Hello, World",
}
http.Handle("/", server)
log.Fatal(http.ListenAndServe(":8080", nil))
}
http.HandleFunc関数を使用する書き方
- http.HandleFunc関数にルートパスと、http.ResponseWriter と *http.Request を引数とする関数を渡す。この関数の中に目的の処理を実装する。
- 下記のソースコードは、Go Designe Patterns より引用。
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World")
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
http.HandleFunc関数の中身を追う
- https://golang.org/src/net/http/server.go を読む。
- http.Handle関数も、http.HandleFunc関数も最終的には以下の(mux *ServeMux)Handle関数を呼び出して、http.Handlerインタフェースを登録する。
// Handle registers the handler for the given pattern.
2341 // If a handler already exists for pattern, Handle panics.
2342 func (mux *ServeMux) Handle(pattern string, handler Handler) {
- http.Handle関数の場合、内部で(mux *ServeMux)Handle関数を直接呼んでいる。
// Handle registers the handler for the given pattern
2372 // in the DefaultServeMux.
2373 // The documentation for ServeMux explains how patterns are matched.
2374 func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
- http.HandleFunc関数の場合、(mux *ServeMux)HandleFunc関数を呼び出し、その関数の中で、(mux *ServeMux)Handleが呼び出されている。その際に、func(ResponseWriter, *Request)を満たす関数として渡されたhandlerは、HandlerFunc型にキャストして渡されている。
// HandleFunc registers the handler function for the given pattern
2377 // in the DefaultServeMux.
2378 // The documentation for ServeMux explains how patterns are matched.
2379 func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
2380 DefaultServeMux.HandleFunc(pattern, handler)
2381 }
// HandleFunc registers the handler function for the given pattern.
2367 func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
2368 mux.Handle(pattern, HandlerFunc(handler))
2369 }
- HandlerFunc型は、ServeHTTPメソッドを持つため、http.Handlerインタフェースとして使用することができる。http.HandleFunc関数に渡されたfunc(ResponseWriter, *Request)の型の関数は、最終的にhttp.HandlerFunc型にキャストすることで、http.Hanlderインターフェースに変換されている。
// The HandlerFunc type is an adapter to allow the use of
1940 // ordinary functions as HTTP handlers. If f is a function
1941 // with the appropriate signature, HandlerFunc(f) is a
1942 // Handler that calls f.
1943 type HandlerFunc func(ResponseWriter, *Request)
1944
1945 // ServeHTTP calls f(w, r).
1946 func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
1947 f(w, r)
1948 }
http.HandlerFunc型にキャストすれば、http.Handle関数も使えるのでは
- 使えた。
- http.HandleFunc関数の意義は、キャストを省略すること・・・?
func main() {
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World")
}))
log.Fatal(http.ListenAndServe(":8080", nil))
}
まとめ
- Go Design Patterns の中では、以下のように書かれており、http.HandleFunc関数が Adapter としているが、実際の Adapter としての役割は、http.HandlerFunc型が果たしている気がした。
The HandleFunc function is actually part of an adapter for using functions directly as ServeHTTP implementations.
- GOの標準ライブラリのソースコードを読んだことがあまりなかったので、良いトレーニングになった。
- 今回初めて記事を書いたが、本を読むだけで理解した気になっていたことが、記事を書くうちに勘違いして理解していた部分などに気づけた(http.HandleFunc関数とhttp.HandlerFunc型を区別できていなかった)。実際のソースコードの中身を見たり、他の方の記事を読むことで理解が深まった。
参考にさせていただいた記事
- net/http の動きを少しだけ追ってみた - Golang
- net/http のソースコードが解説されており、理解の助けになりました。