LoginSignup
34
21

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-14

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

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

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

34
21
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
34
21