構造体に関数を埋め込むとは
以下のように構造体にメソッドを生やすことが多いと思う。
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