kazu22002の技術覚書

PHPer, Golang, AWS エンジニアの日々

golangの構造体について

golangを始めた時に、一番混乱したのは構造体でした。

packageによるnamespaceについて理解はできましたが、構造体の利用についてはかなり悩みました。

ただ構造体の活用を理解した瞬間からコードが構造的になったと思います。PHPの知識が活かせるようになったと思います。

言語が変わればその言語の仕様や文化にそった方がいい。というのもわかりますが、いままでの知識を活かした工夫ができるのであればきっかけになると思います。

構造体についてはgolangでもC言語でもちゃんとした知識があるといいと思うので、整理してみます。

構造体=classというイメージになったときにできることが広がったと思います。

classの考え方を活かしたコードの説明をしてみます。

構造体宣言

type TypeA struct {
    name string
    value int64
}

t := new TypeA()

メタ情報をつけた宣言

type TypeA struct {
    name string `json:"name" form:"name"`
    value int64 `json:"value" form:"value"`
}

メタ情報を活用することで、変数名とkeyを変えたりできます。jsonの読み込みや出力の際にjsonキーを元にしたデータにできます。

メタ情報を読み込むこともプログラムでできます。独自のキーを指定することもできるので、活用できるようになると便利です。

コンストラク

type TypeA struct {
    name string
    value int64
}

// New
func NewTyepA(p string ) *TyepA {
    return &TyepA{
        name: p,
    }
}

t := NewTypeA("test")

最初大文字の指定のため、他のパッケージからも呼び出しできます。コンストラクタとして呼び出しできるため、必要に応じて最初小文字の構造体で作成すれば、呼び出しできなくできます。

type typeA struct {
    name string
    value int64
}

// New
func NewTypeA(p string ) *typeA {
    return &typeA{
        name: p,
    }
}

これで呼び出しにはこの関数を呼ぶしかできなくなり、コンストラクタっぽくなります。

関数

構造体には関数を追加することが可能です。構造体に指定するパターンは2つあります。ポインタありとポインタなしの2つです。

type TypeA struct {
    name string
    value int64
}

func (t TypeA) Name() string {
    return t.name
}

func (t TypeA) Value() int64{
    return t.value
}
type TypeA struct {
    name string
    value int64
}

func (t *TypeA) Name() string {
    return t.name
}

func (t *TypeA) Value() int64{
    return t.value
}

func (t *TypeA) SetName( p string ){
    t.name = p
}

func (t *TypeA) SetValue( p int64 ){
    t.value = p
}

「(t *TyepA)」の部分がレシーバ引数といい、構造体に付与する関数になっています。ポインタがつくことで、構造体の中身を編集できるようになります。

レシーバ引数にポインタが付いていない状態で値の書き換えを関数内で行っても、値が変わりません。

A tour of goで解説されている内容が細かく理解しやすいです。

go-tour-jp.appspot.com

プロパティ

構造体で指定した変数はプロパティだと思ってください。レシーバ引数に関してはポインタで作っておけば、いつものイメージ通りのプロパティとして使用できます。

type TypeA struct {
    name string
    value int64
}

func (t *TypeA) SetName( p string ){
    t.name = p
}

インターフェースの利用

type TypeGroup interface {
    SetName( string ) 
}

type TypeA struct {
    name string
    value int64
}

func (t *TypeA) SetName(p string) {
    t.name = p
}

type TypeB struct {
    name string
    value int64
    add int64
}

func (t *TypeB) SetName(p string) {
    t.name = p
}

// New
func NewTypeA(t int64) TypeGroup {
    if t == 2 {
        return &TypeB{}        
    }
    return &TypeA{}
}

factoryパターンとかできます。

以上です。