Go
golang

golangのreflectとinterface

golangのreflectとinterfaceを色々と触ってみたので備忘録。

reflect

まずはreflectからですが動的に型を扱いたいってやつです。
が、チートシートがまとめられてたので特に説明不要かなと、、、実際に書いてみると理解しやすいかなと思いました。
Go 言語 reflect チートシート

interface

メソッドリストみたいな言い方をされたりしてますが、golangのjavaのinterfaceみたいに色々と書かなくても簡単に使えます。よく見る例ですがこんなかんじ。

package main

import (
    "fmt"
)

type Animal interface {
    Cry() string
}

type Dog struct{}

func (d *Dog) Cry() string {
    return "わん"
}

type Cat struct{}

func (c *Cat) Cry() string {
    return "にゃん"
}

func Sound(a Animal) {
    fmt.Println(a.Cry())
}

func main() {
    Sound(&Dog{})
    Sound(&Cat{})
}

ただ個人的にこのコードに違和感があり、色々と調べてみたところ、mixinっぽい?traitっぽい?ことができそうでした。

mixin?trait?

golangでmixinというのもおかしいかもしれませんが、phpのtraitみたいなものをinterfaceを使って実装することができます。上のサンプルコードに少し手を加えてみます。

package main

import (
    "fmt"
)

type IAnimal interface {
    Cry() string
}

type Animal struct {
    IAnimal
}

type Dog struct{}

func (d *Dog) Cry() string {
    return "わん"
}

type Cat struct{}

func (c *Cat) Cry() string {
    return "にゃん"
}

func main() {
    dog := Animal{&Dog{}}
    fmt.Println(dog.Cry())

    cat := Animal{&Cat{}}
    fmt.Println(cat.Cry())
}

構造体で宣言していたDogとCatには手を加えていませんが、新たにAnimalという構造体を追記してます。中身はinterfaceをそのまま突っ込んでるかんじです。
もちろん名前をつけることもできますが、その場合呼び出すときに名前を記述する必要がでてきます。

type Animal struct {
    inf IAnimal
}
func main() {
    dog := Animal{&Dog{}}
    fmt.Println(dog.inf.Cry())
}

動的に...reflectを使って...

としたかったのですが、結果的にはどこかで明示的に型を書かないとうまく動かなかったという結果でした。やりたかったのはDBとかに格納してる値を元にインスタンス生成して関数をコールしようとしましたが、、、
構造体に値を格納するところをreflectで書いたところまではよかったのですが、最初の処理でswitchで分けてしまってるという結果。

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

type IContainer interface {
    MethodA() string
    MethodB(x, y int) int
}

type Driver struct {
    IContainer
}

type DriverType int

const (
    ADD = "ADD"
    MINUS = "MINUS"
)

func CreateDriver(driver string) IContainer {
    switch driver {
    case ADD:
        return &Add{}
    case MINUS:
        return &Minus{}
    }
    return nil
}

func New(driver string, args ...interface{}) *Driver {
    container := CreateDriver(driver)
    rt, rv := reflect.TypeOf(container).Elem(), reflect.ValueOf(container).Elem()
    for i, arg := range args {
        field := rt.Field(i)
        value := rv.FieldByName(field.Name)
        value.Set(reflect.ValueOf(arg))
    }

    return &Driver{container}
}

type Add struct {
    Value int
}

func (c *Add) MethodA() string {
    return strconv.Itoa(c.Value)
}
func (c *Add) MethodB(x, y int) int {
    return (x + y) + c.Value
}

type Minus struct {
    Value int
    Digit int
}

func (c *Minus) MethodA() string {
    return fmt.Sprintf("%d, %d", c.Value, c.Digit)
}
func (c *Minus) MethodB(x, y int) int {
    return c.Value - (x+y)*c.Digit
}

func main() {
    a := New("ADD", 193)
    fmt.Println(a.MethodA())
    fmt.Println(a.MethodB(9, 3))

    m := New("MINUS", 200, 3)
    fmt.Println(m.MethodA())
    fmt.Println(m.MethodB(3, 9))
}

Go Playground

いろいろと書いて勉強にはなりましたが、まだまだ理解ができてないなと思ったかんじです。
良い書き方などあれば是非。