私の意見では、Goでのライブラリの作成は非常に明るいトピックですが、アプリケーション(コマンド)の作成に関する記事ははるかに少ないです。 それに関しては、Goコードはすべてコマンドです。 それについて話しましょう! この投稿は、シリーズの最初のものとなります。 まだ共有していない情報がたくさんあります。
今日の記事では、プロジェクトの基本レイアウトに焦点を当て、再利用とコードテストを改善します。
ライブラリではなくプログラムを開発するとき、コードを整理するための3つの独自のルールがあります。
メインパッケージ
これはGoのプログラムで唯一必要なパッケージです。 go
ツールに実行可能ファイルを作成するよう指示する以外に、このパッケージにはもう1つユニークなものがあります-コードをインポートすることは不可能です。 これは、 main
パッケージに入れたコードを別のプロジェクトで直接使用できないことを意味し、これはオープンソースソフトウェアの神々を混乱させます。 私がオープンソースプロジェクトを書く主な理由の1つは、他の開発者がそれを使用できることであるため、 main
からコードを再利用できないことmain
、私の望みに反しています。
多くの場合、「コードでプログラムXのロジックを使用する」と考えたときの状況がありました。 しかし、ロジックがmain
パッケージにある場合、これは不可能でした。
os.Exit
ユーザーが期待することを正確に実行するプログラムを作成することに関心がある場合は、終了コードが何であるかに注意する必要があります。 これを行う唯一の方法は、 os.Exit
を呼び出すことos.Exit
(または、 log.Fatal
ようなlog.Fatal
呼び出すものを呼び出します)。
ただし、 os.Exit
を呼び出す関数をテストすることはできません。 なんで? テストの実行中にos.Exit
呼び出すと、テスト中のアプリケーションが終了するためです 。 あなたが偶然それを持っているかどうかを検出することは非常に困難です(私はこれを個人的な経験から知っています)。 テストを開始すると、実際にはテストは行われず、これらのテストは本来の予定よりも早く完了し、頭をひっかくだけで済みます。
できる最も簡単なことはos.Exit
を呼び出さないことです。 ほとんどの場合、ほとんどのコードはos.Exit
呼び出すべきではありません...ライブラリをインポートする場合、誰かが本当に「屋根から出る」ことができ、特定の条件下でランダムにアプリケーションを停止します。
したがって、最小限のエントリポイントで、アプリケーションの「外観」に可能な限り近い1か所でos.Exit
呼び出します。 ところで、それらについて話しましょう...
func main()
これは、Goで記述されたプログラムが持つべき唯一の機能です。 すべてのプログラムが異なるため、各main
機能はプログラムごとに異なるはずだと思いますか? コードをテスト可能かつ再利用可能にしたい場合、「主な機能には何があるのか?」という質問に対する正解は、概して1つだけであることがわかります。
少し先を見ると、「メインパッケージには何がありますか?」という質問に対する正解も1つしかないと思います。 この答えは次のようになります。
以上です。 これは、あなたの便利なmain
パッケージにあるべき最小限のコードです。 他の人が再利用できないコードにはほとんど労力を費やしませんでした。 同時に、私たちはos.Exit
を単一行関数に分離しos.Exit
た。これはプロジェクトの最も外部的な部分であり、実際にはテストを必要としません。
プロジェクト概要
プロジェクトの一般的なスキームを見てみましょう。
/home/you/src/github.com/you/proj $ tree . ├── cli │ ├── parse.go │ ├── parse*test.go │ └── run.go ├── LICENSE ├── main.go ├── README.md └── run ├── command.go └── command*test.go
main.go
内容はすでにわかっています...実際、 main.go
がmain
パッケージの唯一のgoファイルです。 LICENSE
およびREADME.md
ファイルは一目瞭然です。 (常にライセンスを指定してください!さもないと、多くの人があなたのコードを使用できなくなります。)
次に、2つのサブディレクトリrun
とcli
進みます。
CLI
cli
パッケージには、コマンドライン解析ロジックが含まれています。 ここで、プログラムのインターフェース(UI)を定義します。 パッケージには、フラグ分析、引数分析、ヘルプテキストなどが含まれています。
また、 main
関数( os.Exit
)の終了コードを返すソースコードも含まれています。 したがって、プログラム全体の終了コードをテストする代わりに、これらの関数が返す終了コードをテストできます。
走る
main
cli
とcli
がプログラムロジックの「骨」である場合、 run
パッケージには「肉」が含まれます。 このパッケージは、別個のライブラリであるかのように記述する必要があります。 開発中は、CLI、フラグなどについて考えないでください。 パッケージは構造化データを受信し、エラーを返す必要があります。 別のライブラリ、Webサービス、または他の誰かのプログラムから呼び出すことができると想像してください。 それがどのように使用されるかについて可能な限り少ない仮定をしてください...一般に、それは通常のライブラリであるべきです。
明らかに、大規模なプロジェクトには複数のディレクトリが必要になります。 実際、ロジックを別々のリポジトリに分割できます。 他の人があなたのロジックを再利用したいと思うかどうかに依存します。 これが可能性が高いと思われる場合は、別のディレクトリ(リポジトリ)にロジックを実装することをお勧めします。 私の意見では、ロジック用の別のディレクトリには、リポジトリ内のどこかに隠れているランダムなディレクトリよりも高い品質と安定性が必要です。
すべてをまとめる
cli
パッケージは、 run
パッケージに実装されたロジックのコマンドラインインターフェイスを形成します。 誰かがあなたのプログラムを見て、そのロジックをWeb APIに使用したい場合、 run
パッケージをインポートし、このロジックを直接使用するだけです。 同様に、誰かがあなたのコマンドラインオプションを気に入らない場合、彼は自分の引数パーサーを書いて、 run
パッケージへのインターフェースとしてそれを使うことがrun
ます。
これは、コードの再利用について話すときの意味です。 コードをもっとハッキングするために誰かがコードを「ハッキング」する必要はありません。 また、コードの再利用を容易にする最良の方法は、インターフェイスをロジックから分離することです。 これが重要な部分です。 インターフェース(UI / CLI)からのアイデアがロジックに漏れないようにしてください 。 これは、一般的なロジックとインターフェイスを管理しやすくするための最良の方法です。
より大きなプロジェクト
このスキームは、中小規模のプロジェクトに適しています。 リポジトリのルートにある唯一のプログラムは次のとおりです。したがって、複数のサブディレクトリにある場合よりも簡単に取得(取得)できます。 大規模なプロジェクトでは、状況は大きく異なる場合があります。 いくつかの実行可能ファイルが存在する場合がありますが、それらをすべてリポジトリのルートに配置することはできません。 ただし、そのようなプロジェクトには通常、カスタムビルドステップがあり、go-getだけでは不十分です(これについては後で説明します)。
詳細はすぐになります。
2016年10月18日
シリーズ:Goアプリケーション開発。
注:このようなシリーズは発生しませんでした。これは、出版以来の「シリーズ」の唯一の記事です。 しかし、この記事は非常に興味深いものです。