Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13427] ファイルの概要

このコミットは、Go言語のコマンドラインツール cmd/go におけるHTTPクライアントの利用方法に関する変更です。具体的には、httpGet 関数がグローバル変数 httpClient を使用するように修正されています。コミットメッセージには「No change, just for consistency.」とあり、機能的な変更ではなく、コードの一貫性を保つためのリファクタリングであることが示されています。

コミット

commit 35030a996672b3678397f69c1168c4c125393ab2
Author: Amir Mohammad Saied <amir@gluegadget.com>
Date:   Sat Jun 30 12:27:57 2012 -0700

    cmd/go: httpGet function does not use global variable httpClient
    
    No change, just for consistency.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/6346048

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/35030a996672b3678397f69c1168c4c125393ab2

元コミット内容

cmd/go: httpGet function does not use global variable httpClient

No change, just for consistency.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6346048

変更の背景

この変更の背景には、Go言語の標準ライブラリである net/http パッケージの http.DefaultClient の利用と、テスト容易性の向上が関係しています。

元のコードでは、httpGET 関数内で直接 http.Get(url) を呼び出していました。しかし、同じファイル内には httpClient というグローバル変数が http.DefaultClient で初期化されており、コメントには「httpClient is the default HTTP client, but a variable so it can be changed by tests, without modifying http.DefaultClient.」と明記されていました。

このコメントが示すように、httpClient 変数はテスト時にHTTPクライアントの挙動をモックしたり、差し替えたりするために用意されたものです。http.DefaultClient を直接変更すると、アプリケーション全体に影響を与え、並行テストなどにおいて予期せぬ副作用を引き起こす可能性があります。そのため、テストの際に安全にHTTPクライアントを差し替えられるように、httpClient という変数が導入されていました。

しかし、httpGET 関数がこの httpClient 変数を使用していなかったため、その意図が十分に活かされていませんでした。今回のコミットは、機能的な変更を加えることなく、この httpClient 変数の存在意義と、テスト容易性向上のための設計意図に沿って、httpGET 関数が httpClient を利用するように修正することで、コードベース全体の一貫性を高めることを目的としています。

前提知識の解説

Go言語の net/http パッケージ

Go言語の net/http パッケージは、HTTPクライアントとサーバーを実装するための機能を提供します。

  • http.Get(url string) (*Response, error): 指定されたURLに対してHTTP GETリクエストを送信し、そのレスポンスを返します。内部的には http.DefaultClient を使用します。
  • http.DefaultClient: net/http パッケージが提供するデフォルトのHTTPクライアントです。通常、特別な設定が不要な場合に手軽にHTTPリクエストを行うために使用されます。しかし、このグローバル変数を直接変更することは、アプリケーション全体に影響を与えるため、テスト時などには注意が必要です。
  • http.Client 構造体: HTTPリクエストを行うためのクライアントを表す構造体です。TimeoutTransport などのフィールドを通じて、リクエストの挙動を細かく制御できます。
  • Client.Get(url string) (*Response, error): http.Client のメソッドで、指定されたURLに対してHTTP GETリクエストを送信します。

グローバル変数とテスト容易性

プログラミングにおいて、グローバル変数はプログラムのどこからでもアクセスできる変数です。手軽に利用できる反面、以下のような問題を引き起こす可能性があります。

  • 状態の管理の複雑化: どこで変数が変更されたか追跡しにくくなり、デバッグが困難になります。
  • テストの困難さ: テストごとにグローバル変数の状態をリセットする必要があり、テストの独立性を保つのが難しくなります。特に、並行して実行されるテストでは、グローバル変数の共有が競合状態を引き起こす可能性があります。

このコミットで登場する httpClient のように、グローバル変数としてHTTPクライアントを定義し、それをテスト時に差し替え可能にするパターンは、依存性注入(Dependency Injection)の一種と見なすことができます。これにより、http.DefaultClient のような変更が難しいグローバルな依存関係を、テスト可能な形で抽象化しています。

技術的詳細

このコミットは、src/cmd/go/http.go ファイル内の httpGET 関数と httpClient 変数の定義位置に関する変更です。

変更前は、httpClient 変数の宣言が httpGET 関数の後にありました。

// httpGET returns the data from an HTTP GET request for the given URL.
func httpGET(url string) ([]byte, error) {
	resp, err := http.Get(url) // ここで直接 http.Get を呼び出している
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return b, nil
}

// httpClient is the default HTTP client, but a variable so it can be
// changed by tests, without modifying http.DefaultClient.
var httpClient = http.DefaultClient // 変数の宣言が関数の後

変更後、httpClient 変数の宣言が httpGET 関数の前に移動し、かつ httpGET 関数内で http.Get(url) の代わりに httpClient.Get(url) を使用するように修正されました。

// httpClient is the default HTTP client, but a variable so it can be
// changed by tests, without modifying http.DefaultClient.
var httpClient = http.DefaultClient // 変数の宣言が関数の前

// httpGET returns the data from an HTTP GET request for the given URL.
func httpGET(url string) ([]byte, error) {
	resp, err := httpClient.Get(url) // httpClient を使用するように変更
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return b, nil
}

この変更は、以下の2つの側面で「一貫性」を高めています。

  1. 宣言順序の一貫性: httpClient 変数が httpGET 関数内で使用されるため、その宣言を関数の前に移動させることで、コードの読みやすさと論理的な順序を改善しています。Go言語の慣習として、使用される変数は使用される前に宣言されるのが一般的です。
  2. 設計意図との一貫性: httpClient 変数がテスト容易性のために導入されたものであるというコメントの意図に沿って、実際に httpGET 関数がその変数を使用するように修正されました。これにより、将来的にこのコードをテストする際に、httpClient を差し替えることで httpGET の挙動を制御できるようになります。

機能的には何も変更されていませんが、コードの保守性、可読性、そしてテスト容易性が向上しています。

コアとなるコードの変更箇所

--- a/src/cmd/go/http.go
+++ b/src/cmd/go/http.go
@@ -20,9 +20,13 @@ import (
 	"net/url"
 )
 
+// httpClient is the default HTTP client, but a variable so it can be
+// changed by tests, without modifying http.DefaultClient.
+var httpClient = http.DefaultClient
+
 // httpGET returns the data from an HTTP GET request for the given URL.
 func httpGET(url string) ([]byte, error) {
-	resp, err := http.Get(url)
+	resp, err := httpClient.Get(url)
 	if err != nil {
 		return nil, err
 	}
@@ -37,10 +41,6 @@ func httpGET(url string) ([]byte, error) {
 	return b, nil
 }
 
-// httpClient is the default HTTP client, but a variable so it can be
-// changed by tests, without modifying http.DefaultClient.
-var httpClient = http.DefaultClient
-
 // httpsOrHTTP returns the body of either the importPath's
 // https resource or, if unavailable, the http resource.
 func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) {

コアとなるコードの解説

上記のdiffは、src/cmd/go/http.go ファイルに対する変更を示しています。

  • 行 20-23 (+で始まる行):

    +// httpClient is the default HTTP client, but a variable so it can be
    +// changed by tests, without modifying http.DefaultClient.
    +var httpClient = http.DefaultClient
    

    この部分は、httpClient 変数の宣言とそのコメントを、httpGET 関数の定義の直前に移動したことを示しています。これにより、httpGET 関数が httpClient を使用する前に、その変数が宣言されていることが明確になります。コメントは、この変数がテストのために http.DefaultClient を変更せずに差し替え可能であることを説明しています。

  • 行 26 (-で始まる行):

    -	resp, err := http.Get(url)
    

    この行は削除されたことを示しています。元のコードでは、httpGET 関数内で net/http パッケージのグローバル関数 http.Get を直接呼び出していました。

  • 行 27 (+で始まる行):

    +	resp, err := httpClient.Get(url)
    

    この行は追加されたことを示しています。削除された行の代わりに、httpClient グローバル変数の Get メソッドを呼び出すように変更されています。これにより、httpGET 関数は、テスト時に差し替え可能な httpClient を介してHTTPリクエストを行うようになります。

  • 行 37-40 (-で始まる行):

    -// httpClient is the default HTTP client, but a variable so it can be
    -// changed by tests, without modifying http.DefaultClient.
    -var httpClient = http.DefaultClient
    

    この部分は、httpClient 変数の元の宣言位置が削除されたことを示しています。これは、変数の宣言がファイルのより適切な位置(httpGET 関数の前)に移動したためです。

全体として、この変更は機能的な動作を変えることなく、コードの構造と意図をより明確にし、テスト容易性を向上させるためのリファクタリングです。

関連リンク

  • Go CL 6346048 - このコミットに対応するGoのコードレビューシステム(Gerrit)のチェンジリスト。

参考にした情報源リンク