Goブログ

Goコードの整理

Andrew Gerrand
2012年8月16日

はじめに

Goコードは他の言語とは異なる方法で構成されています。この記事では、Goプログラムの要素に名前を付け、パッケージ化して、ユーザーにとって最適なサービスを提供する方法について説明します。

適切な名前を選択する

選択した名前は、コードの考え方に関わるため、パッケージとそのエクスポートされた識別子の命名には注意が必要です。

パッケージの名前は、その内容のコンテキストを提供します。たとえば、標準ライブラリのbytesパッケージBuffer型をエクスポートします。単独では、Bufferという名前はあまり記述的ではありませんが、パッケージ名と組み合わせると、その意味は明確になります:bytes.Buffer。パッケージ名がutilなど、あまり記述的でない場合、バッファはutil.BytesBufferというより長く、扱いにくい名前になる可能性があります。

作業中に名前の変更をためらわないでください。プログラムに時間を費やすことで、プログラムの部分がどのように連携しているか、そしてそれらの名前がどうあるべきかをよりよく理解できるようになります。初期の決定に固執する必要はありません。(gofmtコマンドには、構文を認識する検索と置換を提供する-rフラグがあり、大規模なリファクタリングを容易にします。)

適切な名前は、ソフトウェアインターフェースの最も重要な部分です。名前はコードのすべてのクライアントが最初に目にするものです。したがって、適切に選択された名前は、優れたドキュメントの出発点となります。次の多くのプラクティスは、適切な命名から自然に生じます。

適切なインポートパスを選択する(パッケージを「go get」可能にする)

インポートパスは、ユーザーがパッケージをインポートするために使用する文字列です。パッケージのソースコードが存在するディレクトリ($GOROOT/src/pkgまたは$GOPATH/srcを基準とする)を指定します。

インポートパスはグローバルに一意である必要があるため、ソースリポジトリのパスをベースとして使用します。たとえば、go.netサブ・リポジトリのwebsocketパッケージのインポートパスは"golang.org/x/net/websocket"です。Goプロジェクトは"github.com/golang"パスを所有しているため、別の著者が異なるパッケージにそのパスを使用することはできません。リポジトリURLとインポートパスが同じであるため、go getコマンドはパッケージを自動的に取得してインストールできます。

ホストされたソースリポジトリを使用しない場合は、ドメイン、会社、またはプロジェクト名などの独自のプレフィックスを選択します。例として、Googleの内部Goコードのすべてのインポートパスは"google"という文字列で始まります。

インポートパスの最後の要素は、通常、パッケージ名と同じです。たとえば、インポートパス"net/http"にはパッケージhttpが含まれています。これは必須ではありませんが、予測可能性のために規約に従うべきです。ユーザーは、"foo/bar"をインポートすると、識別子quuxがパッケージのネームスペースに導入されることに驚くかもしれません。

ソースリポジトリのルートにGOPATHを設定し、パッケージをリポジトリルートを基準とするディレクトリ(例:"src/my/package")に配置する人がいます。一方では、これによりインポートパスが短くなります("my/package"ではなく"github.com/me/project/my/package")が、他方ではgo getが機能しなくなり、ユーザーはパッケージを使用するためにGOPATHを再設定する必要があります。これは行わないでください。

エクスポートされたインターフェースを最小限にする

コードは多くの小さな便利なコードで構成されている可能性が高いため、パッケージのエクスポートされたインターフェースに多くの機能を公開したくなります。その衝動に抵抗しましょう!

提供するインターフェースが大きいほど、サポートする必要があるものも多くなります。ユーザーはすぐに、エクスポートするすべての型、関数、変数、および定数に依存するようになり、永続的に遵守する必要がある暗黙的な契約を作成するか、ユーザーのプログラムを壊すリスクを負うことになります。Go 1を準備する際に、標準ライブラリのエクスポートされたインターフェースを注意深くレビューし、コミットする準備ができていない部分を削除しました。独自のライブラリを配布する際にも、同様の注意を払う必要があります。

疑問がある場合は、省略しましょう!

パッケージに入れるもの

すべてを「何でもあり」のパッケージにまとめてしまうのは簡単ですが、これによりパッケージ名の意味が薄まります(多くの機能を包含する必要があるため)また、パッケージの一部を使用するユーザーは、多くの関連のないコードをコンパイルしてリンクする必要があります。

一方、コードを小さなパッケージに分割しすぎるのも簡単で、その場合は、仕事を終えるのではなく、インターフェース設計に巻き込まれる可能性があります。

Go標準ライブラリをガイドとして参照してください。パッケージの中には大きく、中には小さいものがあります。たとえば、httpパッケージは17個のGoソースファイル(テストを除く)で構成され、109個の識別子をエクスポートし、hashパッケージは3つの宣言のみをエクスポートする1つのファイルで構成されています。厳格なルールはありません。どちらのアプローチも、そのコンテキストによっては適切です。

つまり、パッケージmainは他のパッケージよりも大きいことがよくあります。複雑なコマンドには、実行可能ファイルのコンテキスト外ではほとんど役に立たない多くのコードが含まれており、通常はすべてを1か所にまとめておく方が簡単です。たとえば、goツールは、34個のファイルにわたって12000行以上です。

コードを文書化する

優れたドキュメントは、使用可能で保守可能なコードの重要な品質です。Godoc:Goコードの文書化の記事を読んで、優れたdocコメントの書き方を学びましょう。

次の記事:App Engine 1.7.1のGoのアップデート
前の記事:GCC 4.7.1のGccgo
ブログインデックス