Golang Cafe #19を開催しました。
今回は、
Twelve Go Best Practicesの資料を読むということで、(恐らく)その6まで読み進めました。
ソースコードと、プラクティスのメモは
githubにありますので、ごらんください。
commitログと履歴を遡ればどういう変化があるかを見ることができるようになっています。
以下、今日のまとめです。資料と共に読み進めて下さい。
その1
深いネストは避けよう!
Goは例外機構がないので、全ての呼び出しについて、エラーチェックが必要になります。
(エラーチェックじゃなくても、戻り値を受け取った後に何らかのif文が必要になる)
そこで、全ての(仮にここではerrorをチェックするとして)エラーをif文で囲むとネストが深くなりやすくなります。
では、どうするかというと、”エラーがある場合は、即return”ということになります。
こうすることで、ネストが深くなるということが避けられます。
その2
構造体に必要なオブジェクトを格納することで、シンプルなコードに!
その1で”エラーがある時は即return”としたのですが、「毎回チェック」をする必要はないわけです。というと、errorを構造体のメンバにしておき、戻り値をメンバに退避しておきます。
そして、メソッド呼び出しの最初に、メンバのerrorがnilかどうかをチェックし、nilでなければ、即終了させる。という形にします。
これにより、呼び出し側でエラーを確認する必要がなくなるので、シンプルな呼び出しにすることができます。
その3
型で処理が決まる場合は、型で処理を分岐させましょう!
型によって、処理がわかれる場合に、switch v.(type) {}を使うと、型の判定を行うことができます。実は、標準パッケージだと、よく書かれている記述で、ユーティリティを作るのに非常に便利です。(Go以外の言語で型による分岐って無い気がするので、独自のパターンかもしれません)
引数は、interface{}で受け取ることがポイントです。
その4
switch文の:=を使え!
その3で型による分岐が出てきましたが、実際に処理を進めるに当たって、Type Assertionをする必要があるのですが、v.(type)については、Goの構文として短縮構文にすると、case文は型で判定し、値は短縮構文の時にType Assertionされているという状況を作ることができます。したがって、型によって、Type Assertionをする必要がなくなります。
その5
全て書くか、何も書かないかのどちらかにする。
データベースのトランザクションと同じような感じですが、例えば、通信プログラムなど、「途中まで成功」というのは都合がよくありません。そこで、一旦、bytes.Bufferに送信電文のデータを書き出しておき、Flush()メソッドなどで、一括で送信するというようにしておくと、良いですよという話。
しかも、このソースコードは、再帰呼び出しを使っていて、エラーのチェックも1か所に集約されています。が、これは日本(の中小企業(?))だと怒られるのではないか?という話になりました。慣れないとすぐには気がつかない…w
その6
リクエストを処理し、エラーを返す関数と、エラーを受け取り、エラーを処理する関数に分ける!
ここから、Webサーバの例になりますが、複数の処理に対するエラーの処理をどうするか。というのがテーマだと思います。(実は、私もGAE/Gで同じ失敗をしているのですが…)
毎回if文でエラーチェックして、エラーレスポンスの処理を書くとイケてないコードになります。(これは、私も疑問に思っていた)そこで、エラーを返すだけの主処理と、主処理を呼び出し、エラーチェック後、レスポンスを返す、エラー処理関数を用意します。
すると、エラー処理がきれいに集約されてきれいになります。
また、errorを変数で宣言しておくと、エラーの判定により、レスポンスを変える事ができるので非常に便利です。ただ、資料のfmt.Errorf()は、内部でerrors.New()をしているので、一見便利に見えますが、エラー判定ができなくなるので、
使うと後で困ることになるので使わないようにしようということでした。
今回は、資料の12ページまで読み進めたので、次回は、13ページから読み進める予定です。