この記事はtraP Advent Calendar 2021 27日目の記事です。
この記事は「interface何も分からん」ってなっている人が、ざっくりinterfaceの気持ちを理解するために書かれています。よって正確性に欠けることがあります。
あなたは誰? : いろり(@irori)。sysAd班,グラフィック班
できること : golang,mysql(諸説あり)
できないこと : フロントエンド全般
はじめに
golangのコードを読むとちょくちょくインターフェイスが出てきます。
ここでインターフェイスの理解が中途半端だと、コードの意味を見失ったりします(n敗)。
iroriは部内イベント管理サービスであるknoQのバックエンドを担当しているのですが、knoQ進捗会でのfuji先輩とwasabi先輩のinterfaceの説明がわかりやすかったので、残しておきたいと思います。
例
//Circle(円)という構造体を定義 *1
type Circle struct {
Radius int
}
//Circleのメソッドを定義 *2
func (c Circle) GetArea() int {
return 3 * c.Radius * c.Radius
}
//Square(正方形)という(ry
type Square struct {
Height int
}
func (s Square) GetArea() int {
return s.Height * s.Height
}
//Figure(図形)インターフェイスを定義 *3
type Figure interface {
GetArea() int
}
//Figureの面積を表示する関数を定義 *4
func DisplayArea(f Figure) {
fmt.Printf("面積は%vです\n", f.GetArea())
}
//実行される部分 *5
func main() {
circle := Circle{Radius: 1}
DisplayArea(circle)
square := Square{Height: 3}
DisplayArea(square)
}
コードの解説
*1
circleという構造体を定義している。
構造体とクラスはほぼ同じと考えていいと思う。RPGの村人クラスだったら、年齢と性別があるのと同じように、円クラスは半径という数値を持つ(と定義している)。
(型と同様に考えても良いと思う。つまりint型、string型、circle型という感じ)
*2
円クラスのメソッドを定義している。これは円の面積を求めるメソッド。
メソッドは円クラスのもののみが使える関数というイメージでオッケー。
Square(正方形)も同様です。
*3
インターフェイスを定義している。
書き方はこう。
type 名前 interface {
メソッド メソッドでreturnされる型
}
意味は「Figureというのは引数なし,返り値intのメソッドGetArea()
を持つインターフェイスです」ということ。
「なぜメソッドなのか?」、「どういう意味があるのか?」は後述。
*4
Figureインターフェイスのみで使える関数を定義している。引数はインターフェイス。
(正確にはインターフェイスの条件を満たすクラスの何かです)
*5
goではファイルを実行した時に
func main(){}の {} の中が実行されます。(ド基礎。よく忘れる...)
つまりここが実行される場所。
circle := Circle{Radius: 1}
ではCircle型(クラスの意)のcircleが作られている。そしてそのcircleには半径1が代入されている。
(RPGを例に出すと村人型の村人Aが作られ、年齢15歳が代入されているイメージ。)
DisplayArea(circle)
ではcircleの面積が計算され、「面積は3です」と表示されます。
実行の流れ
前提
- circleはCircle型
- Circle型はメソッドGetArea()を持つ。よってFigureインターフェイスに含まれる
- Figureインターフェイスに含まれるのでDisplayArea()が実行できる
🆗
処理
circle := Circle{Radius: 1}
で半径1のcircleが作成される
- DisplayArea(circle)では、関数DisplayAreaが呼び出され
fmt.Printf("面積は%vです\n", circle.GetArea())
が実行される
circle.GetArea()
は(circleの半径が1だったから)面積の計算結果3とが返却される
fmt.Printf("面積は%vです\n", 3)
になるから「面積は3です」と表示される
この例を踏まえて
同じメソッドを持つ型に共通する関数を作りたいんだなー とわかればOK
つまりGetArea()
がラベルのような働きをしている!
- Circle型
GetArea()
- Square型
GetArea()
GetArea()
を持つ型のみが関数DisplayArea を実行できるよ〜 ということ。
そして、「GetArea()
を持つ」という条件をFigureインターフェイスといいます。(ちょっとざっくり言いすぎかもしれない)
疑問
何でメソッドをラベルに代わりにするんだ? ラベルみたいなものを直接定義すればよくないか?
つまり
Q.
- Circle型
@Figure
- Square型
@Figure
ラベル@Figure
が貼ってある型はDisplayArea を実行できる
みたいにした方がスマートだし、柔軟性が効くのでは?
A.
例えば@Figure
があり、@Figure
がついている型に実行できる関数があったとしよう。
その時にその関数は何ができるだろうか?
@Figure
は何らかのメソッドを持つことを保証するものではないので、@Figure
の全てで 〜ができるという保証がない
逆に、GetArea()
のメソッドを持つ型をFigureインターフェイスとすれば、Figureインターフェイスは少なくともGetArea()
を使用できることが保証されている。
interfaceで呼び出された関数は,interfaceで定義されたメソッドを使うことを基本的に前提としている
interfaceは(意味由来)やりたいことから作られている
のである!
(とiroriは理解した)
次(今日)の担当はiroriさんです。お楽しみに〜(???)
明日の担当はryohaさんです。楽しみ〜