【Go言語】embeddedと継承は一緒にしてはいけない

## はじめに
Goではオブジェクト指向プログラミングの一つである「**継承**」という概念はありません。
しかし、以下の機能を使えばオブジェクト指向言語っぽくGoを扱うことができます。
- **embedded(埋め込み)を使った実装**
- **interfaceの使用**

つまり、これらのものを使用すれば継承のようなことが可能ですが、`embedded`は注意深く扱う必要があります。
本記事ではこの`embedded`について深く触れていきます！

まずは、埋め込みについて復習していきましょう！
## embedded(埋め込み)とは
まずは、「embedded」とは何なのかというところから話していこうと思います。
`embedded`とは構造体に他の構造体を埋め込むことができる機能です。
次の、`Vertex`構造体を例に考えていきましょう。
```golang
type Vertex struct {
	X, Y int
}

//XとYの積を計算して、その結果を返します
func (v Vertex) Area() int {
	return v.X * v.Y
}

//Vertex構造体のインスタンスを作成し、アドレスを返します
func New(x, y int) *Vertex {
	return &Vertex{x, y}
}

func main() {
	v := New(3, 4)
 // 12が出力されます
	fmt.Println(v.Area())
}
```
まず、`Vertex` 構造体を定義し`Area`メソッドと`New`関数を使ってインスタンスを作成します。
引数として与えた値を辺とし、2次元の面積を返すものになっていることがわかるかと思います。
ではこれを、3次元に拡張したい場合に`embedded(埋め込み)`を使用します。
```golang
type Vertex struct {
	X, Y int
}

func (v Vertex) Area() int {
	return v.X * v.Y
}

// Vertex3D 構造体を作成し、"Z" フィールドを追加
type Vertex3D struct {
	Vertex
	Z int
}

func (v Vertex3D) Area3D() int {
	return v.X * v.Y * v.Z
}

func New(x, y, z int) *Vertex3D {
	return &Vertex3D{Vertex{x, y}, z}
}

func main() {
	v := New(3, 4, 5)
	fmt.Println(v.Area())
 // 60が出力されます
	fmt.Println(v.Area3D())
}
```
[The Go Playground](https://go.dev/play/p/LShmC4gVQJh)：こちらで試してみましょう！

このように`Vertex3D`構造体に、`Vertex`構造体を埋め込むことで`Vertex3D`構造体は、「`X`, `Y`フィールド」と「`Area`メソッド」を持つことができます。
これが**Embedded**です。

それでは本題に入っていきます。
## なぜ、Embeddedと継承を一緒にすべきでないのか
### embeddedにはオーバーライドの概念が存在しない
Goには、オブジェクト指向プログラミングで多くのデザインパターンの基礎となっているメソッドのオーバーライドがありません。
先ほどのコードを例に考えると、`Vertex3D`が`Vertex`のメソッドをオーバーライドするわけではなく、単に`Vertex`のメソッドをそのまま使用しているだけなんです。

上記のコードに以下を追加してみましょう。
```golang:main.go
func (v Vertex3D) Area() int {
	return v.X * v.Y * v.Z
}
```
この時、main関数を実行した時、以下のAreaメソッドはどちらの出力でしょうか。
```golang:main.go
func main() { 
    v := New(3, 4, 5) 
     // この出力はどちらのAreaメソッドか？
    fmt.Println(v.Area())
    }
```
[The Go Playground](https://go.dev/play/p/GUkPYtJYnY2)：こちらで実行してみましょう！
結論として、`Vertex3D`構造体に定義された`Area`メソッドが優先されます。
Go言語では、埋め込まれた構造体のメソッドもそのまま使用することができるため、`Vertex3D`は`Vertex`のAreaメソッドにアクセスできます。
ただし、`Vertex3D`に独自のAreaメソッドを追加しても、それが`Vertex`のメソッドを「オーバーライド」するわけではなく、`Vertex3D`自身のメソッドとして扱われます。

別の`Parent`構造体を例に考えていきましょう！
```golang
package main

import "fmt"

type Parent struct{}

func (p Parent) m1() {
	//親は子に埋め込まれているが、親は子のことを知らないので、親のm2が呼ばれる
	p.m2()
}

func (p Parent) m2() {
	fmt.Println("Parent")
}

type Child struct {
	Parent
}

func (c Child) m2() {
	fmt.Println("Child")
}

func main() {
	c := Child{}
	c.m1()// Parent：オーバーライド失敗
	c.m2()// Child
}
```
[The Go Playground](https://go.dev/play/p/EhiY_tuNG_1)：こちらで実行してみましょう！
上記のコードを一個ずつ追っていきましょう。
- `Parent`構造体は「m1」・「m2」という二つのメソッドを持っています
- 「m1」メソッド内でp.m2()が呼ばれていますが、これは`Parent`構造体のm2メソッドを呼び出します
- `Child`構造体は`Parent`構造体を埋め込んでおり、独自に「m2」メソッドを持ちます

main関数を実行すると、
`c.m1()`で「m1」メソッド内で`p.m2()`が呼ばれますが、これは`Parent`構造体の「m2」メソッドを呼び出すため、`Parent`と出力されます。
`c.m2()`では、Child 独自の m2 メソッドが使用され`Child`と出力されます。
こちらの例から分かる通り、

:::note  
**ポイント！**
`Parent`構造体の「m1」メソッドは、自身の「m2」メソッドを呼び出しているのであって、埋め込みによって`Child`構造体の「m2」メソッドがオーバーライドされるわけではありません。これが、オブジェクト指向の「継承」とは異なる挙動を示すポイントです。
:::

### 「 *継承* 」ではなく「 *委譲* 」
Goにおいて、`embedded`は継承の機能ではなく、単なる「丸投げ」のような動作をします。オブジェクト指向プログラミングでは、このように自分の処理を他のオブジェクトに丸投げする方法を「**委譲（delegation）**」と呼びます。

つまり、Goは「*継承*」の代わりに「*委譲*」を直接サポートする言語機能を採用しているのです。


## まとめ
`embedded`は、あくまでフィールドやメソッドの再利用に過ぎず、オーバーライドのような機能は持ちません。
`embedded`の利用には慎重さが求められます。例えば、親構造体のメソッドが子構造体内のメソッドを呼び出すことはできませんし、その逆もまた然りです。

`embedded`を適切に扱っていきましょう！！！


## 参考

https://www.oreilly.co.jp/books/9784873119694/