Go言語入門:効果的なGo -Interfaces-

スポンサーリンク
Go言語入門:効果的なGo -Interfaces- ノウハウ
Go言語入門:効果的なGo -Interfaces-
この記事は約6分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、Go言語を効果的に使うためのガイドラインについて解説しています。

スポンサーリンク

背景

Go言語を学び始めて、より良いコードを書きたいと思い、Go言語の公式ドキュメント「Effective Go」を知りました。これは、いわば「Goらしいコードの書き方指南書」になります。単に動くコードではなく、効率的で保守性の高いコードを書くためのベストプラクティスが詰まっているので、これを読んだ時の内容を備忘として残しました。

インターフェース

Goのインターフェースは、オブジェクトの振る舞いを指定する方法を提供します:何かが「これ」を行えるなら、「ここ」で使用できるということです。すでにいくつかの簡単な例を見てきました。カスタムプリンターはStringメソッドで実装でき、FprintfWriteメソッドを持つものなら何でも出力を生成できます。1つか2つのメソッドのみを持つインターフェースはGoのコードでよく見られ、通常はメソッドから派生した名前が付けられます。例えば、Writeを実装するものに対するio.Writerなどです。

1つの型は複数のインターフェースを実装できます。例えば、コレクションはsort.Interfaceを実装していればsortパッケージのルーチンによってソートできます。このインターフェースにはLen()Less(i, j int) boolSwap(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]のような形式で表示

重要なポイント

  1. 暗黙的な実装:Goでは「implements」のような明示的な宣言は不要です。必要なメソッドを持っていれば、自動的にそのインターフェースを実装したことになります。
  2. 小さなインターフェース:Goでは1〜2個のメソッドを持つ小さなインターフェースが推奨されます。
  3. 複数のインターフェース実装:1つの型が複数のインターフェースを同時に実装できます。上の例では、Sequenceは以下を実装しています:
    • sort.InterfaceLen, Less, Swapメソッド)
    • fmt.StringerStringメソッド)
  4. レシーバーによるメソッド定義func (s Sequence)の部分で、Sequence型にメソッドを追加しています。

この仕組みにより、柔軟で再利用可能なコードが書けるようになります。

おわりに 

本日は、Go言語を効果的に使うためのガイドラインについて解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました