この記事はGo その3 Advent Calendar 2015の18日目です。
仕事の関係でGoを使い始めて気付けば9ヶ月。今ではすっかり慣れましたが、始めた当初はそれまでC言語・Java中心で仕事をしてきた自分にとってGoの文化はかなり異質に映ったのを覚えています。
せっかくなので、他言語経験者でこれからGoを始めますよという方向けに、「Goではこんなことしても許されるよ」みたいなポイントを挙げてみたいと思います。
1文字の変数名
昔のプログラミングの入門書のコード例なんかだと、
int a, b, c;
というように1文字の変数名が多用されてたりして、それを引きずったまま職業プログラマになってコードレビューで可読性警察に逮捕される、なんてことは良くあったのではないでしょうか。
最近は「リーダブルコード」などの良い書籍が普及したのもあり、変数名は多少長くとも意味が判るものが良い、という文化が一般的なのではないかな、と思います。例えばこんな感じ。
int theAnswerToTheUltimateQuestionOfLife = 42;
ところがGoのソースコードを読んでみると1文字の変数名がバリバリ使われてます。
// strings.Join関数のコードを抜粋
func Join(a []string, sep string) string {
if len(a) == 0 {
return ""
}
if len(a) == 1 {
return a[0]
}
n := len(sep) * (len(a) - 1)
for i := 0; i < len(a); i++ {
n += len(a[i])
}
b := make([]byte, n)
bp := copy(b, a[0])
for _, s := range a[1:] {
bp += copy(b[bp:], sep)
bp += copy(b[bp:], s)
}
return string(b)
}
この辺はGoの公式ドキュメントの一端でもきちんと触れられていて
Variable names in Go should be short rather than long. This is especially true for local variables with limited scope. Prefer c to lineCount. Prefer i to sliceIndex.
(GoCodeReviewCommentsのVariable Namesより抜粋)
と、「スコープが限られたローカル変数に限り」短い変数名を推奨しているんですね。確かに、先述のコード内のnやbといった変数も数行しか使われていないので、あまり読みにくいと感じることはありません。
シンプルさを尊重するGoの文化が良く表れてますね。
グローバル変数
グローバル変数って今では親の仇のように嫌われてますよね。自分から使う人はもうあまりいないと思いますが、JavaScriptでうっかりvarつけ忘れてコードレビューでけちょんけちょんにされるなんてことは未だにあると思います。少なくとも僕が見つけたらそうします。
function add(left, right) {
result = left + right;
return result;
}
でもGoだと気にせずガンガン使います。ガンガンは言い過ぎだけど割と使っても平気です。パッケージ名で綺麗に名前空間が区切られますものね。
クラスの概念がないGoだとパッケージ全体を静的クラスと同等のものとみなして、グローバル変数を静的フィールドのように使うこともあります。例えばシングルトンパターンを実現しようとすると以下のようなものになります。
(Singleton Pattern in Goより抜粋)
package singleton
import (
"sync"
)
type singleton struct {
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
うん、これグローバル変数というよりただのパッケージプライベートな変数だ!
許してください、なんでもしますから・・・
パブリックな例はosパッケージとか見れば結構見つかるんじゃないですかね(投げやり)
1テストメソッド複数アサーション
多くの言語のテストメソッドで良く言われるアンチパターンとして、1メソッドに複数のassertを書いてしまう、というものがあります。
@Test
public void testXXX() {
hoge = new Hoge();
assertThat(hoge.getName(), is("ほげ"));
assertThat(hoge.getWife(), nullValue());
//・・・
}
これが嫌われる理由は以下のような点にあります。
- 途中のassertで失敗すると、以降のassertが行われない
- テストメソッドが単一責務の原則に違反する可能性がある
まあ後者に該当するようなケースは素直にメソッド分けした方が良いと思いますが、少なくとも先述のコードはオブジェクトのプロパティの初期値のassertをしているに過ぎず、複数メソッドに分ける価値は薄いように見えます。かといって一々特化したassertメソッドを作るのはめんどくさい。
でも、Goなら気にせず以下のように書いてしまえばいいんですね。
func TestXXX(t *testing.T) {
hoge := hoge.New()
if hoge == nil {
t.Fatal("Failed to create hoge.")
}
if hoge.Name != "ほげ" {
t.Errorf("hoge.Name => %s, wants %s", hoge.Name, "ほげ")
}
if hoge.Wife != nil {
t.Errorf("hoge.Wife => %v, must be nil", hoge.Wife)
}
}
テストメソッドを中断しないError系のメソッドと、中断するFatal系のメソッドを使い分けることで実に気楽にテストが書けます。testingパッケージ最強や!
JavaならHogeクラスのequalsメソッドをオーバーライドしとけ?
C言語ならGoogle C++ Testing Frameworkでも出来る?
はて・・・
もっとあったと思いますが、いざ考えるとなかなか挙げられないものです・・・。
思い出したらまた増やします。