Go言語文化圏に踏み出す前に
Go言語ですが、「ただ一つのことをうまくやる(Do one thing and do it well)」という思想がある言語です。
つまり、かなり設計思想や原則を守った開発を求められることになります。
もし、Go言語文化圏について学ばずにGo言語のソフトウェアに足を踏み出してしまうとGo言語文化圏では厳しく罰せられるかもしれません。
もし、Go言語の原則を知らないまま開発を進めてしまうと、待っているのはチームや自分自身の疲弊、保守性の悪いセンスのないソフトウェア、のような地獄になるかもしれません。
そんな地獄には足を踏み入れたくない。そんな私がGo言語文化圏について学んだことをシェアしようと思います。
対象の読者
- Go言語知らないけど、どんな特徴があるかは抑えたい人
- Go言語をこれから勉強しようと思っている人、初学者
この記事のゴール
- Go言語の命名規則をざっくりと把握する
- Go言語書いたことがなくても、「Go言語って、〜なところがある言語だよね」とドヤ顔で雑談ができる
参考書籍
Go言語の異文化感を命名規則から感じる
では、Goらしく良いコードを書くために命名規則を学んでいきましょう!
変数名
書籍を読んで学んだこと。
- 変数名はMixedCaps(maxLength)で書くこと
- ただし、外部から参照できるパブリックな変数は先頭大文字(MaxLength), プライベートな変数は先頭小文字(maxLength)
-
頭字語は全て大文字か全て小文字とする(URL, url等)
- 他のごと組み合わせる場合にはMixedCaps(ServeHTTP, stackId)
- エラーの変数名、には接尾語Errorをつけること(MarshalerError, UnsupportedTypeError...)
- errors.New("my error")のように宣言されるエラーの変数は、Err or errから始まる
- 長い名前よりも短い名前が好まれる
- HTTPリクエストを扱う変数名はrequestよりもreqとされることが多い
- ただし、短ければ良いというわけではなく、宣言した場所と離れた場所で用いられる変数については説明的な変数をつける必要がある
変数名の定義はかなり重要な部分なので、Goのスタイルガイドの「スタイル決定事項」からの情報も紹介します。
- 参照されるスコープに応じて変数名は長くすべきであるため、コードのスコープは極力狭くなるようにし、余計な情報を避けられるように書くべし
- 適切にスコープが管理されていれば、1単語か1~2文字で問題ないかもしれません
-
useCount
やprojectCount
のように似たような名前の曖昧さをなくすために単語を追加しても良い - 入力の手間を省くためにただ文字を削除しないようにすること(例: ❌:
Sandbox
をSbx
のようにすること) - 周囲の文脈から明らかな単語を省略すること
-
UserCount
というメソッドの実装で、userCount
というローカル変数を使うことは冗長であると言える
-
- 一文字の変数名は、完全な単語が明らかであるかつ、その変数が繰り返し記述されるような場合の使用にすること
- 後述するが、レシーバでは1文字の名前をつける
- 整数型のループ変数では、1文字でもOK(forループの
i
など)
頭字語についても、全て小文字or大文字になるのは読みやすいですね。他の言語で頭字語の命名が決まってるのはあまり見たことはありませんが、Go言語のルールの方が読みやすそうです。
また、変数名については単に短くすれば良いというわけではなく、読みやすさを考慮した上で変数を短くすべきである。と読み取りました。
そのため、Go言語文化圏では、上記のルールに則らないと怒られます。
Goに入ればGoに従え、の通り、しっかりと覚えて守っていきましょう。
パッケージ名
書籍を読んで学んだこと。
- パッケージ名は小文字で構成される一つの単語にしましょう
- 複数のようとで用いられる場合はフォルダを分ける(例: encoding/json)
- パッケージ名からどのようなものなのかがわかるように命名しましょう
- ❌ util, common, api
- ⭕️ json, http
- もし複数の用途で使用されるパッケージを作る場合は、encoding/jsonのようにフォルダを分けるようにしましょう
- 複数の名刺を組み合わせたくなるような名前だったら、黙ってフォルダを分けよう(encoding/json, encoding/xmlなど)
- パッケージ名と関数名はDRY原則に従うこと
- ❌ http.HTTPServer
- ⭕️ http.Server
読んでいてかなり合理的だなと思いつつ、最初の「パッケージ名は小文字で構成される一つの単語にしましょう」を守れるかはかなり不安なところではありますね。
参考書籍ではなく、Go言語のスタイルガイドを見ると、tabwriter
のような命名も許されるようです。
そのため、一つの単語しか使ってはいけないわけではなく、全てを小文字で表現した上で伝わるような命名ができる程度にシンプルな命名にしましょう。それができるくらいのシンプルな設計にしましょう。ということだと思います。
インターフェース名
- 1つのメソッドのみを持つインターフェースでは「er」という接尾辞がついた名前が用いられることがあります。
- 例:o.Reader, fmt.Stringer
- 「er」がついたせいで英語として正しくない場合もOK(ExecerContexxt)
- 複数のメソッドを持つインターフェースである場合は「そのインターフェースの目的を適切に説明する名前を選択しましょう」
- 例: net.Conn, http.ResponseWriterなど
思ったこと
- 動詞部分にerがつくんですね
- Goの思想である、ただひとつのことをうまくやる。を考えるとインターフェースが持つメソッドは理想は1つのみということかもしれない。
レシーバ名
そもそもレシーバって何?
Go言語素人の私はここからの勉強でした。
ざっくりと説明すると、
- 特定の型のオブジェクトに紐づけた関数を定義できるようにするもの
- 値レシーバとポインタレシーバがある
- 値レシーバ: レシーバの値を変更できない
- ポインタレシーバ: レシーバの値を変更できる
という感じです。オブジェクト指向でいう、クラスやクラスメソッドのようなものを実現するための機能だと解釈しています。
// サンプルコード
package main
import (
"fmt"
)
// Person - 構造体
type Person struct {
Name string
Age int
}
// Describe - 値レシーバーを持つPersonのメソッド
func (p Person) Describe() {
fmt.Printf("%s is %d years old.\n", p.Name, p.Age)
}
// Birthday - ポインターレシーバーを持つPersonのメソッド
func (p *Person) Birthday() {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
alice.Describe() // Alice is 30 years old.
alice.Birthday()
alice.Describe() // Alice is 31 years old.
}
命名の特徴
-自身を表すレシーバにはレシーバの型を反映した名前をつけるのが望ましく、通常は1~2文字で十分
例: Requestならr
, Regexpならre
一般的に略字は悪だと思うが、メソッド内に閉じてるので良いだろう。という考え方なんでしょうね。
メソッドの宣言時に略字が何を表しているのかが書いてある(サンプルコードでいうと、p Person
)ので、略字でも読みやすさが落ちません。
極力シンプルにするのが望ましい、というGo言語の思想が出ている命名ですね。
定数
書籍を読んで学んだこと
-
const
を使って定義する - ミックスキャップで命名される(例:
MaxPacketSize
)
書籍だけではなくスタイルガイドから学んだことも追記
- 定数には値ではなく役割に基づいて名前をつける(例: ❌
const Seven = 7
, ⭕️const DaysOfWeek = 7
)
命名については基本的には変数と同じ感じのようです。
まとめ
Go言語の命名規則について書いてみました。
もし誤り等があれば優しくご指摘ください。命名規則以外もまとめられればと思うのでまとめ次第記事を追加しようと思います。