【Go】学習メモ1:インターフェースとダックタイピング

アプリケーションを作りたくてGo言語を学んでいます。
所々躓いた点は自分なりに理解した(と思われる)内容やイメージを残していきたいと思います。
如何せん初心者なもので、もし理解が誤っていたら教えていただけると幸いです・・・!

インターフェースのイメージ

インターフェースは次のようなイメージを持ってます。

f:id:k-bind:20200524122208p:plain
インターフェースのイメージ

上から、人類→個人→メソッドといったイメージです。
【インターフェース】
Humanは「喋る」、「歩く」、「寝る」などのメソッドを持っています。(ここではまだ「喋る」や「歩く」がどのような動作なのかは分からない)
【ストラクト】
Personは構造体を表します。(ここでは個人の宣言と名前や年齢などの属性を用意しています。歩くや寝るの動作もあるので、ストレス状況や筋力などの属性も用意してもいいと思います!)
【メソッド】
先ほどの通りHumanに属するPersonはインターフェースにて宣言した「喋る」や「歩く」が出来ます。ここでは『喋るとは?歩くとは?』どのような動作を意味するのかを記述します。

つまり、
①予めインターフェース内にて人型が可能な動作を宣言しておき、
②人型に属する個人が ③インターフェース内で定義した動作をメソッドとして用意する。
そうすることで、人型に割り当てた個人は、いつでもインターフェース内で割り当てた動作を呼び出すことが可能となる。
そんな便利さを持ち合わせているのがインターフェース。でいいのでしょうか(汗

先ほどの図を別の観点からイメージすると下のようになります。
f:id:k-bind:20200524122237p:plain

このように、Person(struct)をHuman(Interface)に代入することで、HumanであるPersonはPreson.Say()が実行可能となります。
ある個人は"人型"であり、"人型"は喋るので、ある個人は"喋れる"ということです。
またHuman(Interface)にSleep()などほかのメソッドも宣言したら同様にPersonで使える関数として定義しなければなりません。

実装

これから上のイメージの通り実装していきます。
①予めインターフェース内にて人型が可能な動作(今回はSay()のみ)を宣言しておき、
②人型に属する個人(属性は名前のみ)が ③行えるインターフェース内で定義した動作をメソッドとして用意する。
の順番で進めます。

①予めインターフェース内にて人型が可能な動作(今回はSay()のみ)を宣言しておき、

package main

import "fmt"

type Human interface {
	Say()
}

②人型に属する個人(属性は名前のみ)が

type Person struct {
	Name string
}

③行えるインターフェース内で定義した動作をメソッドとして用意する。

func (p Person) Say() {
	fmt.Println(p.Name)
}

あとはmain関数内で、Humanにmikeという名前のPersonを代入してsayを実行する。

func main() {
	var mike Human = Person{"mike"}
	mike.Say() //"mike"
}

ダックタイピング

"If it walks like a duck and quacks like a duck, it must be a duck"
(もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない)
ja.wikipedia.org

つまり、
"Person("mike")がインターフェースHumanで定義したSay()の関数を持ち合わせているのであれば、PersonもインターフェースHumanを満たしている"ということです。(恐らく・・・)

分かりにくいので例えば、mikeがレンタカーで車を予約し、"Mr.mike"なら乗車可能、それ以外なら乗車不可といったメッセージがアウトプットされるコメントを出力したいとします。
この場合、Person.Say()には戻り値を持たせたいので
①のインターフェースのメソッドSay()の戻り値を文字型に

type Human interface {
	Say() string
}

③のSay()には戻り値を、頭に"Mr."をつけるため、Personの中身を変更するのでポインタレシーバー(*Person)に変更

func (p *Person) Say() string {
	p.Name = "Mr." + p.Name
	return p.Name
}

新たにDriveCarという関数を定義。

func DriveCar(human Human) {
	if human.Say() == "Mr.mike" {
		fmt.Println("ok")
	} else {
		fmt.Println("get out!")
	}
}

mikeという構造を持った人型(Human)のPerson(個人)がSay()のメソッドを持ち合わせているため、
DriveCar関数が実行することができました。
先ほどSay()をポインタレシーバとしたので、Person{"mike"}にも&をつけます。

func main() {
	var mike Human = &Person{"mike"}
	var x Human = &Person{"x"}
	DriveCar(mike) // "ok"
	DriveCar(x) // "get out!"
}

今回は以上になります。