LoginSignup
1
1

More than 5 years have passed since last update.

http.HandleFunc関数におけるAdapterとしての実装の理解

Last updated at Posted at 2018-03-18

概要

  • 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型を区別できていなかった)。実際のソースコードの中身を見たり、他の方の記事を読むことで理解が深まった。

参考にさせていただいた記事

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1