前書き
Go とデザインパターンの並行勉強がてら、Builder パターンを書いてみたらちょっと詰まったので覚書き。
外部パッケージに定義したインタフェースを実装してみたらうまく動かなかったので、インタフェースとポインタについて調べてみた。
Builder パターン
足したり引いたりするだけの、GoF Builder の最低限の実装(だと思う)。
NewDirector でインスタンスを生成し、Construct で実行。
builder.go
package builder
type builder interface {
Add(num float64)
Sub(num float64)
GetResult() string
}
type Director struct {
builder builder
}
func NewDirector(b builder) Director {
d := Director{b}
return d
}
func (d *Director) Construct() {
b := d.builder
b.Add(100)
b.Sub(50)
b.Add(100)
}
builder パッケージを main.go で実装し、Director で実行結果を得る。
SumBuilder は Builder インタフェースを実装した実体。
main.go
package main
import (
"fmt"
"strconv"
"./builder"
)
type Director builder.Director
type SumBuilder struct {
sum float64
}
func NewSumBuilder() *SumBuilder {
s := &SumBuilder{0}
return s
}
func (s *SumBuilder) Add(num float64) {
s.sum += num
}
func (s *SumBuilder) Sub(num float64) {
s.sum -= num
}
func (s *SumBuilder) GetResult() string {
sumStr := strconv.FormatFloat(s.sum, 'g', 8, 64)
return "Sum: " + sumStr + "."
}
func main() {
sumbuilder := NewSumBuilder()
director := builder.NewDirector(sumbuilder)
director.Construct()
str := sumbuilder.GetResult()
fmt.Printf("%v\n", str)
}
実行するとちゃんと Sum: 150 が出力される。
躓きポイント
前述の builder.go は修正後のコード。修正前はこんな感じ。
builder.go(修正前)
/* 省略 */
type Director struct {
builder *builder
}
func NewDirector(b *builder) *Director {
d := &Director{b}
return d
}
func (d *Director) Construct() {
b := *d.builder
b.Add(100)
b.Sub(50)
b.Add(100)
}
ポイントは Director がメンバで持っている builder インタフェースをポインタで持っていたこと。
どうやらインタフェースのポインタはインタフェースとしての機能を持てないということらしい。
つまりインタフェースの機能を実装した実体のポインタを渡してもインタフェースとしては振る舞えないので意味がない、ということだ。
結論
結局のところは、ポインタについてもっとちゃんと考えましょうね、というお話でした。
参考
Go言語のインターフェイスとポインタについての小実験
http://d.hatena.ne.jp/eel3/20140915/1410788174