- Goには継承という概念がない。そのため構造体をうまく利用して埋め込み作業をしていく。
- Goでは構造体の中に構造体を埋め込むことで再利用性を高めたり、コード量を減らすことが出来る。
- person型とcitizen型はどちらも独立しており、親子関係にはならない。(is-a関係)
main.go
package main
import "fmt"
type person struct {
Name string
}
type citizen struct {
Country string
person
}
func (p person) greet() string {
return fmt.Sprintf("Hello, %s", p.Name)
}
func main() {
c := citizen{
Country: "japan",
}
c.Name = "hoge" // c.person = &person{ Name: "hoge" } でも同じ
// func(p Person)Greet()string が呼びだされる。
// func(c Citizen)Greet()string を定義した場合はこっちが呼ばれる。
greeting := c.greet()
fmt.Println(greeting)
}
- 上記の例を違う関数から間接的に呼び出そうとすると失敗する
main.go
package main
import "fmt"
type person struct {
Name string
}
type citizen struct {
Country string
person
}
func (p person) greet() string {
return fmt.Sprintf("Hello, %s", p.Name)
}
// この関数を追加
func speak(p person) string {
return p.greet()
}
func main() {
c := citizen{
Country: "japan",
}
c.Name = "hoge"
// これはエラーになる
greeting := speak(c)
// => speak(c.person) これはうまくいく
fmt.Println(greeting)
}
- 埋め込まれている構造体でもそのまま呼び出せるようにするにはインターフェースを活用する
main.go
package main
import "fmt"
type person struct {
Name string
}
type citizen struct {
Country string
person
}
type greeter interface {
greet() string
}
func (p person) greet() string {
return fmt.Sprintf("Hello, %s", p.Name)
}
// interfaceを使って引数を定義する
func speak(g greeter) string {
return g.greet()
}
func main() {
c := citizen{
Country: "japan",
}
c.Name = "hoge"
p := person{
Name: "fuga",
}
greeting1 := speak(c)
greeting2 := speak(p)
fmt.Println(greeting1) // Hello, hoge
fmt.Println(greeting2) // Hello, fuga
}
- 上記のようにすることでコードが見やすくなったり、共通化を行いやすくなる
-
interface
自体は関数をまとめて一つのオブジェクトを作ることができる便利なものである
参照
Goはオブジェクト指向言語だろうか?
オブジェクト指向言語としてGolangをやろうとするとハマる点を整理してみる-Qiita
インタフェースの実装パターン #golang