embedの練習
goで機能を引き継いだ感じで実装
type Animal struct {
Name string
}
func (a *Animal) Eat(something string) {
fmt.Print(a.Name, "は", something, "を食べた")
}
type Cat struct {
Animal
}
CatはAnimalを実装した。
継承では無いので複数実装できて便利。
type Animal struct {
Name string
}
type Enjoy struct {
Playing bool
}
type Cat struct {
Animal
Enjoy
}
動物で、遊びが好きな猫
動作テスト
func main() {
cat := &Cat{}
cat.Name = "ボス猫"
cat.Eat("お菓子")
}
ボス猫はお菓子を食べた
CatはEatできた。
メソッドを乗っ取る overrideっぽい感じで
func (c *Cat) Eat(something string) {
c.Animal.Eat(something)
fmt.Println("ニャー by", c.Name)
}
func main() {
cat := &Cat{}
cat.Name = "ボス猫"
cat.Eat("お菓子")
}
ボス猫はお菓子を食べたニャー by ボス猫
テストコードは変えずに、乗っ取った感じになった。
乗っ取られた後に、拡張元を呼び出す
cat := &Cat{}
cat.Eat("お菓子")
cat.Animal.Eat("お菓子")
ボス猫はお菓子を食べたニャー by ボス猫
ボス猫はお菓子を食べた
別の形で渡す
cat := &Cat{}
var a *Animal
a = cat
継承では無いのでもちろんダメ。複数実装できるし。
下のように部分的に渡すのはOK
cat := &Cat{}
var a *Animal
a = &cat.Animal
インターフェースで渡すのもOK
cat := &Cat{}
var a interface{}
a = cat
cat := &Cat{}
var a AnimalAction
a = cat
type AnimalAction interface {
Eat(somthing string)
}
cat := &Cat{}
cat.Name = "ボス猫"
var a AnimalAction
a = cat
a.Eat("お菓子")
ボス猫はお菓子を食べたニャー by ボス猫
実際に機能を拡張してみる
webの処理で使うtemplate.Templateを拡張する
package mytemplate
import "html/template"
type Template struct {
*template.Template
}
func Must(t *template.Template, err error) *Template {
temp := template.Must(t, err)
return &Template{
Template: temp,
}
}
紛らわしいけど「Template」は拡張したもの。
同じ名前にしておくと後々便利。 import template "mytemplate"
って書くと入れ替わるし。
「template.Template」は拡張前のもの。
tm := template.Must(template.ParseFiles("templates/" + filename))
tm := mytemplate.Must(template.ParseFiles("templates/" + filename))
自分の構造体に持ってこれたので、後は自由。
毎回、同じデータをテンプレートに渡す機能を実装してみる
func (t *Template) Execute(wr io.Writer, data map[string]interface{}) error {
data["stylesheet"] = template.HTML(`<link rel="stylesheet" type="text/css" href="semantic.min.css">`)
return t.Template.Execute(wr, data)
}
tm := template.Must(template.ParseFiles("templates/" + filename))
tm.Execute(w, map[string]interface{}{
"list": list,
})
tm := mytemplate.Must(template.ParseFiles("templates/" + filename))
tm.Execute(w, map[string]interface{}{
"list": list,
})
もちろん、他のメソッドはそのまま利用可能。
パッケージ名を入れ替えるだけで実装を変えられる。(紛らわしさも残るが)
継承では無いけど、機能を引き継いで拡張できた。
機能の引き継ぎとは関係なくなる
一応ビルドは通る。
ただし、実行時に怒られるパターン。
type Cat struct {
AnimalAction // ここをインターフェースに変えた
}
type AnimalAction interface {
Eat(somthing string)
}
func main() {
cat := &Cat{}
cat.Eat("お菓子")
}
panic: runtime error: invalid memory address or nil pointer dereference
結局interfaceという受け皿を用意しているだけで何も入っていないから、ビルドは通るけど設定していないという事。
この方法を使うと、Eatの実装が気分次第で入れ替え可能。
type ActionHungry struct {
}
func (*ActionHungry) Eat(somthing string) {
fmt.Println(somthing, "を食べた")
}
type ActionFull struct {
}
func (*ActionFull) Eat(somthing string) {
fmt.Println(somthing,"を食べなかった")
}
func main() {
cat := &Cat{}
cat.AnimalAction = &ActionHungry{}
cat.Eat("お菓子")
cat.AnimalAction = &ActionFull{}
cat.Eat("お菓子")
}
お菓子 を食べた
お菓子 を食べなかった
cat.Eat()の挙動が変わった。
単に普通のインターフェース使った実装に変わっただけ。引き継いでない。