Go言語における埋め込みによるインタフェースの部分実装パターン

September 19, 2013

この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。

構造体

Go言語には、クラスがない。そのため、代わりに構造体(struct)を使い、その型にメソッドを設けることで似たような振る舞いをさせる。

http://play.golang.org/p/WosVAwNGad

package main

import "fmt"

type Person struct {
	FirstName string
	LastName  string
}

func (p *Person) Name() string {
	return p.FirstName + " " + p.LastName
}

func main() {
	person := &Person{"Taro", "Yamada"}
	fmt.Println(person.Name())
}

インタフェース

Go言語にはインタフェースがある。明示的に実装しなくてもインタフェースが定義するメソッドをすべて実装していれば、そのインタフェースを実装していることになる(ダックタイピング)。

http://play.golang.org/p/hqHGJZek3a

package main

import "fmt"

type Person struct {
	FirstName string
	LastName  string
}

func (p *Person) Name() string {
	return p.FirstName + " " + p.LastName
}

type Named interface {
	Name() string
}

func printName(named Named) {
	fmt.Println(named.Name())
}

func main() {
	person := &Person{"Tarou", "Yamada"}
	printName(person)
}

構造体の埋め込み

Go言語の構造体はクラスではないため、継承ができない。しかし、埋め込みを使うことで他の構造体をラップした構造体を作ることができる。 中に埋め込んだ構造体のフィールドやメソッドはあたかも外の構造体のもののように振る舞うことができる。

http://play.golang.org/p/VyKJCRdEFB

package main

import "fmt"

type Hoge struct {
	N int
}

type Piyo struct {
	Hoge
	M int
}

func main() {
	piyo := &Piyo{Hoge{1}, 2}
	fmt.Println(piyo.N, piyo.M)
	fmt.Println(piyo.Hoge.N, piyo.M)
}

埋め込みを使ったインタフェースの部分実装

埋め込む構造体にあるインタフェース定義するメソッドの一部を実装させ、残りのメソッドは外側の構造体で実装するという手法が使える。下の例だと、Nameメソッドはperson構造体で共通の実装をして、Titleメソッドはfemaleとmaleで別の実装をしている。また、Personインタフェース以外はパッケージ外にエクスポートしないことで、使用者に詳細の実装を見せないようにしている。

http://play.golang.org/p/fbARybwPOd

package main

import "fmt"

type Gender int

const (
	Female = iota
	Male
)

type Person interface {
	Name() string
	Title() string
}

func New(gender Gender, firstName, lastName string) Person {
	p := &person{firstName, lastName}
	if gender == Male {
		return &male{p}
	} else {
		return &female{p}
	}
}

type person struct {
	firstName string
	lastName  string
}

func (p *person) Name() string {
	return p.firstName + " " + p.lastName
}

type female struct {
	*person
}

func (f *female) Title() string {
	return "Ms."
}

type male struct {
	*person
}

func (m *male) Title() string {
	return "Mr."
}

func printFullName(p Person) {
	fmt.Println(p.Title(), p.Name())
}

func main() {
	taro := New(Male, "Taro", "Yamada")
	printFullName(taro)

	hanako := New(Female, "Hanako", "Yamada")
	printFullName(hanako)
}