S.Hayakawaをフォローする

【Golang】interfaceは何者だ – Gopherへの道 ②

バックエンド

こんにちは。
Go言語での開発も始まり、そのコーディングの楽しさも少しずつ感じているs.hayakawaです。

前回 から引き続き、Go言語について学んだ内容をまとめていきたいと思います。

ところで、私はGo言語の学習を始めた頃、
Interfaceはいったい何ができるものなのかよく分かりませんでした。

(メソッドの型を定義した型?ポリモーフィズム???)

ということで、今回はGo言語でのInterfaceは何ができて、何のためにあるのか
大まかにでも掴めるようになることを目標に見ていきたいと思います!

はじめにこちらのコードを見ていきましょう。

インターフェースを実装する

type Hoge interface {
	FuncA()
        FuncB()
}

type Foo struct {}

func (f *Foo) FuncA() {}
func (f *Foo) FuncB() {}

func main() {
        var hoge Hoge
        hoge = Foo{..}       // HogeインターフェースにFoo構造体を代入できる
}

Hoge インターフェースを実装してみました。

Golangのインターフェイスとは

  • Interfaceはメソッドの塊です。
  • Interfaceが期待するメソッド(例ではFuncA,FuncB)をすべて満たした変数には、自動的にInterfaceが実装されます。
  • Interfaceを満たした変数はInterfaceへ代入することができます。

インターフェースを言葉で説明しようとするとこんな感じになると思います。
なんとなく理屈は分かりましたが、これを一体何に使うのでしょうか。

インターフェイスの使い方の一例

使い方を体感するために、もう少し具体的なコードで表してみましょう。

以下では、
本(book)に関するDB操作の実装と、これを使用してbookを取得する処理を考えます。

実装側:bookManagement.go
使用側:main.go


今回は使用側では、bookを取得する処理のみ必要と仮定します。

bookManagement.go
package bookManagement

type BookDatabase struct{}

func NewBookDatabase() *BookDatabase {
	var db BookDatabase
	return &db
}

func (d *BookDatabase) GetBook(id string) *Book {}

func (d *BookDatabase) CreateBook(data interface{}) {}
main.go
package main

import "bookManagement"

type BookReader interface {
	GetBook() *Book
}

func main() {
	var db := bookManagement.NewBookDatabase()
	var id = "abcde12345"
	result := fetch(db, id)
}

func fetch(b BookReader, id string) *Book {
	return b.GetBook(id)
}

内容を見ていきましょう。

bookManagementパッケージでは、GetメソッドとCreateメソッドが定義されています。

今回の処理ではBookの取得以外は必要ないため、
GetBookメソッドのみを持ったBookReaderインターフェースを実装しました。

BookReaderインターフェースには、BookDatabase構造体を代入することができます。
(GetBookメソッドを満たしているため)

ここで一つポイントがあります。
main.goのfetch()関数は、bookManagementのGetBookメソッドしか使用することができません。
引数にとっているインターフェイスがGetBookメソッドしか実装していないためです。

インターフェースを使用するメリット

ここで、interfaceを使用する目的が一つ考えられます。
それは、実装と使用を切り離して考えられるということです。

BookReaderインターフェースを引数にもつfetch()関数は、
このインターフェースで宣言しているメソッド以外を使用することはできません。
裏を返せば、fetch()関数は他のパッケージの不要な実装を知ることなく、
必要なメソッドを使用することができているとも言えます。

以上がインターフェイスの使い方の一例です。

こういった使い方をするメリットは以下が考えられます。

  • main.goが不必要なメソッドに依存していない
  • 実装側(bookManagement)に他のメソッドが増えてもmain.goに影響がない

空のインターフェイス

上記はGoのインターフェイスの使い方の一例ですが、

他にも、なにもメソッドを要求しない空のインターフェイス型などもあります。
こちらは冒頭のインターフェイスの説明を見返すと使い方が分かります。

Interfaceを満たした変数はInterfaceへ代入することができます。

空のインターフェイスは何も要求しないので、すべての変数がこのインターフェイスを満たしている
とも言えます。
そのため、どの型の変数でも代入することができます。
Goは静的型付けで型には厳格ですが、型をゆるく扱いたい場合などに有効でしょう。

func main() {
        var i interface{}

        i := 123
        describe(i)

        i := "hoge"
        describe(i)
}

func describe(i interface{}) {
        fmt.Println(i)

        // 出力:123 hoge
}

おわりに

今回は以上です。インターフェイスの使い方はこれ以外にもいろいろアイデアがあるかと思いますが、何となくでも使い方のイメージが掴めていればいいなと思います。最後まで読んでいただいてありがとうございました。次回は並行処理やフレームワーク、DBについてなど書いていければと考えています。

参考:https://go-tour-jp.appspot.com/welcome/1

画像:公式より 
by Renée French