
こんにちは。よっしーです(^^)
本日は、Go言語を効果的に使うためのガイドラインについて解説しています。
背景
Go言語を学び始めて、より良いコードを書きたいと思い、Go言語の公式ドキュメント「Effective Go」を知りました。これは、いわば「Goらしいコードの書き方指南書」になります。単に動くコードではなく、効率的で保守性の高いコードを書くためのベストプラクティスが詰まっているので、これを読んだ時の内容を備忘として残しました。
インターフェース
Goのインターフェースは、オブジェクトの振る舞いを指定する方法を提供します:何かが「これ」を行えるなら、「ここ」で使用できるということです。すでにいくつかの簡単な例を見てきました。カスタムプリンターはString
メソッドで実装でき、Fprintf
はWrite
メソッドを持つものなら何でも出力を生成できます。1つか2つのメソッドのみを持つインターフェースはGoのコードでよく見られ、通常はメソッドから派生した名前が付けられます。例えば、Write
を実装するものに対するio.Writer
などです。
1つの型は複数のインターフェースを実装できます。例えば、コレクションはsort.Interface
を実装していればsort
パッケージのルーチンによってソートできます。このインターフェースにはLen()
、Less(i, j int) bool
、Swap(i, j int)
が含まれており、同時にカスタムフォーマッターを持つこともできます。この作為的な例では、Sequence
が両方を満たしています。
type Sequence []int
// sort.Interfaceで必要なメソッド群
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Copyは、Sequenceのコピーを返します。
func (s Sequence) Copy() Sequence {
copy := make(Sequence, 0, len(s))
return append(copy, s...)
}
// 印刷用のメソッド - 印刷前に要素をソートします。
func (s Sequence) String() string {
s = s.Copy() // コピーを作成;引数を上書きしない。
sort.Sort(s)
str := "["
for i, elem := range s { // ループはO(N²);次の例で修正します。
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}
インターフェースとは何か?
インターフェースは、「このメソッドを持っていれば、この機能を使えますよ」という約束事のようなものです。
例えば、現実世界で考えてみましょう:
- 「運転できる人」なら「車を運転できる」
- 「泳げる人」なら「プールを使える」
Goのインターフェースも同じで、「特定のメソッドを持っている型」なら「そのインターフェースを実装している」と見なされます。
コード例の詳細解説
型定義:
type Sequence []int
これはint
のスライス(配列のようなもの)にSequence
という名前を付けています。
ソート機能を実装する部分(sort.Interface):
func (s Sequence) Len() int {
return len(s)
}
Len()
:スライスの要素数を返します
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
Less(i, j int) bool
:i番目の要素がj番目の要素より小さいかを判定します
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
Swap(i, j int)
:i番目とj番目の要素を入れ替えます
これら3つのメソッドがあることで、sort.Interface
を実装したことになり、sort.Sort(s)
が使えるようになります。
コピー機能:
func (s Sequence) Copy() Sequence {
copy := make(Sequence, 0, len(s))
return append(copy, s...)
}
- 元のスライスを変更せずに、新しいコピーを作成します
make(Sequence, 0, len(s))
:長さ0、容量len(s)の新しいスライスを作成append(copy, s...)
:元のスライスの全要素をコピーに追加
文字列表示機能(fmt.Stringer):
func (s Sequence) String() string {
s = s.Copy() // コピーを作成;引数を上書きしない。
sort.Sort(s)
str := "["
for i, elem := range s { // ループはO(N²);次の例で修正します。
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}
s.Copy()
:元のスライスを変更しないようにコピーを作成sort.Sort(s)
:ソートしてから表示fmt.Sprint(elem)
:各要素を文字列に変換- 結果:
[1 2 3 4 5]
のような形式で表示
重要なポイント
- 暗黙的な実装:Goでは「implements」のような明示的な宣言は不要です。必要なメソッドを持っていれば、自動的にそのインターフェースを実装したことになります。
- 小さなインターフェース:Goでは1〜2個のメソッドを持つ小さなインターフェースが推奨されます。
- 複数のインターフェース実装:1つの型が複数のインターフェースを同時に実装できます。上の例では、
Sequence
は以下を実装しています:sort.Interface
(Len
,Less
,Swap
メソッド)fmt.Stringer
(String
メソッド)
- レシーバーによるメソッド定義:
func (s Sequence)
の部分で、Sequence
型にメソッドを追加しています。
この仕組みにより、柔軟で再利用可能なコードが書けるようになります。
おわりに
本日は、Go言語を効果的に使うためのガイドラインについて解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント