今回は最近実務でも使い出し、学習をしているGo言語のInterfaceについて学習したことをまとめていきたいと思います。
Interfaceは、JavaやC#などの静的型付け言語に登場する概念で、Go言語にも存在します。
これにより実装とInterfaceを分離することができます。
Interfaceは、メソッドの塊です。
Interfaceを定義するには、interfaceキーワードを使用します。
type 名前 interface { メソッド メソッドでreturnされる型 }
Interfaceを定義する際にメソッドは複数定義できます。
interfaceを実装するには、他の言語では明示的に指定する必要がありますが、Go言語では型にメソッドとして全て定義してしまえば、自動的にinterfaceを実装した(要件を満たした)と見なされます。
type Test interface { Hoge() Foo() }
例えば上記のようなinterfaceを定義した場合、以下のように、TestStructにinterfaceを実装しています。
type TestStruct struct {} func (t TestStruct) Hoge() { fmt.Println("hoge") } func (t TestStruct) Foo() { fmt.Println("foo") }
まとめると以下のようになります。
type Profile interface { GetName() string SetName(name string) } type dog struct { Name string } func NewDog() Profile { return &dog{} } func (t *dog) GetName() string { return t.Name } func (t *dog) SetName(name string) { t.Name = name } type Cat struct { Name string } func NewCat() Profile { return &Cat{} } func (t *Cat) GetName() string { return t.Name } func (t *Cat) SetName(name string) { t.Name = name } func main() { t1 := NewDog() t2 := NewCat() makePet(t1, "ポチ") makePet(t2, "ミケ") fmt.Println(t1.GetName()) fmt.Println(t2.GetName()) } func makePet(t Profile, name string) Profile { t.SetName(name) return t }
上記で重要な所は、Dog型であろう、Cat型であろうとと同じく名前をセットできており、同じコードで2つの実体を扱うことができている点です。もしこれがインターフェースではなく具体的な構造体を受け取っていると、それぞれの型で2つの makePet() の定義が必要になってしまいます。
Golangではパッケージを境界として関数や構造体の公開・非公開を制御することができます。これを利用しインターフェースの実装部分のみを外部公開することで、中身の構造体や内部処理に使う関数を隠蔽し特定の型・パッケージに依存したコードを書けなくすることができます。
func makePet(t Profile, name string) Profile { t.SetName(name) return t } type testPet struct { Name string } func (t *testPet) GetName() string { return t.Name } func (t *testPet) SetName(name string) { t.Name = name } func TestMain(t *testing.T) { patterns := []struct { name string }{ {name: "ムギ"}, {name: "ココ"}, {name: "モカ"}, {name: "ソラ"}, } for _, pattern := range patterns { pet := &testPet{} makePet(pet, pattern.name) if pet.GetName() != pattern.name { // エラー処理 } } }
テストファイル内で新たにtestPet型を作成し、Profileインターフェースのメソッドを実装してます。
こういう形を取ることによりテスト時だけ挙動が異なる実装を用意することができるため単体テストを非常にやりやすくすることができます。
上記はGoのinterfaceの使い方の一例ですが、
他にも、なにもメソッドを要求しない空のinterface型などもあります。
空のinterfaceは何も要求しないので、すべての変数がこのinterfaceを満たしているとも言えます。
そのため、どの型の変数でも代入することができます。
func main() { var i interface{} i := 123 describe(i) i := "hoge" describe(i) } func describe(i interface{}) { fmt.Println(i) // 出力:123 hoge }
Goで大きなアプリケーションを作成するための機能としてinterfaceを簡単に学習しました。柔軟性の高いコードを書けるよう、深く勉強していきたい、日々精進していきたいと思います。