LoginSignup
2
1

More than 5 years have passed since last update.

機能を拡張する(go)

Last updated at Posted at 2018-08-11
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

OK
cat := &Cat{}
var a *Animal
a = &cat.Animal

インターフェースで渡すのもOK

OK
cat := &Cat{}
var a interface{}
a = cat
OK
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()の挙動が変わった。
単に普通のインターフェース使った実装に変わっただけ。引き継いでない。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1