Help us understand the problem. What is going on with this article?

Goの構造体のフィールドに関数を埋め込んでDIを実現してみる

More than 1 year has passed since last update.

構造体に関数を埋め込むとは

以下のように構造体にメソッドを生やすことが多いと思う。

package main

import (
    "fmt"
)

type Hoge struct {
}

func (h *Hoge) Foo(bar string) string {
    return fmt.Sprintf("%sだ!", bar)
}

func main() {
    h := &Hoge{}

    fmt.Println(h.Foo("sekky0905"))
}

実行結果

sekky0905だ!

実は、構造体のフィールドに関数を埋め込むことができる。

The Go Programming Language Specification - The Go Programming Languageにも、以下のような例が存在している。

// A struct with 6 fields.
struct {
    x, y int
    u float32
    _ float32  // padding
    A *[]int
    F func()
}

引用元 : The Go Programming Language Specification - The Go Programming Language

なので、以下のようなコードを実装することができる。

import "fmt"

type Hoge struct {
    Foo func(bar string) string
}

func main() {
    f := func(bar string) string {
        return fmt.Sprintf("%sだ!", bar)
    }

    h := &Hoge{Foo: f}

    fmt.Println(h.Foo("sekky0905"))
}

実行結果

sekky0905だ!

使い所

会社の先輩にこの埋め込みの関数の使い所を聞いたところ、「関数をDIで渡したいときとかに使うと便利だよ」
と教えていただいたので、実際に簡単にサンプルを書いてみた。

サンプル

Strategyパターンもどきを実装してみた。

Strategyパターンは、以下のようなもの。

Strategy パターンは、コンピュータープログラミングの領域において、アルゴリズムを実行時に選択することができるデザインパターンである。

引用元 : Strategy パターン - Wikipedia

インターフェースと構造体の埋め込みを用いてDIしたパターンと、関数の埋め込みを用いてDIしたパターンを実装してみたい。

インターフェースと構造体の埋め込みを用いてDIしたパターン

package main

import (
    "fmt"
)

type Strategy interface {
    ExecuteStrategy()
}

type StrategyA struct {
    Name string
}

func (s *StrategyA) ExecuteStrategy() {
    fmt.Printf("%s実行したぜ~!\n", s.Name)
}

func NewStrategyA(name string) Strategy {
    return &StrategyA{
        Name: name,
    }
}

type StrategyB struct {
    Name string
}

func (s *StrategyB) ExecuteStrategy() {
    fmt.Printf("%s実行したよ~!\n", s.Name)
}

func NewStrategyB(name string) Strategy {
    return &StrategyB{
        Name: name,
    }
}

type Player struct {
    Strategy Strategy // Strategy型にすることで、後で埋め込む際にStrategy型を実装した任意の型を埋め込むことができる
}

func main() {
    sa := NewStrategyA("StrategyA")
    p1 := &Player{
        Strategy: sa,
    }

    p1.Strategy.ExecuteStrategy()

    sb := NewStrategyB("StrategyB")
    p2 := &Player{
        Strategy: sb,
    }

    p2.Strategy.ExecuteStrategy()
}

実行結果

StrategyA実行したぜ~!
StrategyB実行したよ~!

ここでのポイントは、Playerに埋め込ませるStrategyフィールドを具体的なStrategyAやStrategyBではなく、Strategy型で定義したことである。こうすることで、Playerをインスタンス化するときに使用するStrategyを選択する余地が生まれる。
(StrategyのフィールドをStrategyAやStrategyBにしちゃうと、どちらかしか埋め込めない)

関数の埋め込みを用いてDIしたパターン

package main

import (
    "fmt"
)

type Player struct {
    ExecuteStrategy func(name string)
}

func ExecuteStrategyA(name string) {
    fmt.Printf("%s実行したぜ~!\n", name)
}

func ExecuteStrategyB(name string) {
    fmt.Printf("%s実行したよ~!\n", name)
}

func main() {
    p1 := &Player{
        ExecuteStrategy: ExecuteStrategyA,
    }
    p1.ExecuteStrategy("StrategyA")

    p2 := &Player{
        ExecuteStrategy: ExecuteStrategyB,
    }
    p2.ExecuteStrategy("StrategyB")
}

結果

StrategyA実行したぜ~!
StrategyB実行したよ~!

ここでのポイントは、PlayerのExecuteStrategyフィールドの関数のシグネチャを満たしたものであれば、どんな関数でもPlayerに埋め込むことができるということである。

参考 : Strategy パターン - Wikipedia

参考にさせていただいたURL

The Go Programming Language Specification - The Go Programming Language

Strategy パターン - Wikipedia

Sekky0905
最近GoとGCPに戻ってきました。 以前は、GAE/Go、AWS、TypeScript、Angular、Vueをやっていました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした