GoFデザインパターン ~Builderパターン~
こちらを読みながら進める。
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (397件) を見る
Builderパターンとは
複雑なインスタンスなどの生成において、骨組みと部品の生成を分離するパターン。
Builderが各部品を作るインターフェースを持ち、監督者であるDirectorがBuilderのインターフェースを使って部品を組み立てていく。
ここでのポイントは、DirectorはBuilderのインターフェースのみ着目するためBuilderの具体的な実装には依存せず、Builderが交換可能であるということ。
クラス図あまり綺麗に書けなかった..
実装
増補改訂版Java言語で学ぶデザインパターン入門で使われてる例をGoで実装してみる。
ディレクトリ構成は以下
./7_builder ├── builder │ ├── builder.go │ ├── html_builder.go │ └── text_builder.go ├── director ├── director.go │ └── document_builder.go └── main.go
Director
構造の生成過程で監督者となるDirector。Builderインターフェスを使って構造を作っていく。コードが複雑になると複数のDirectorが必要になりそうなので、Director-ConcreteDirectorの関係を作りConcreteDirectorはDirectorインターフェースを実装するようにした。こうすることでClientにおいてDirectorも交換可能となる。
package director import "github.com/saekis/go-design-pattern/7_builder/builder" type Director interface { Build() string } type documentDirector struct { builder builder.Builder } func NewDocumentDirector(builder builder.Builder) Director { return &documentDirector{builder: builder} } func (director *documentDirector) Build() string { director.builder.MakeTitle("title") director.builder.MakeString("string") director.builder.MakeItems([]string{ "item1", "item2", }) director.builder.Close() return director.builder.GetResult() }
Builder
Builderで各部品を生成する具体的な実装をおこなう。
Builderインターフェース
package builder type Builder interface { MakeTitle(string) MakeString(string) MakeItems([]string) Close() GetResult() string }
ConcreteBuilder
TextBuilder
package builder import ( "bytes" "fmt" ) type textBuilderImple struct { buf bytes.Buffer } func NewTextBuilder() Builder { return &textBuilderImple{buf: bytes.Buffer{}} } func (b *textBuilderImple) MakeTitle(title string) { b.buf.WriteString(fmt.Sprintf("「%s」\n\n", title)) } func (b *textBuilderImple) MakeString(str string) { b.buf.WriteString(fmt.Sprintf("%s\n", str)) } func (b *textBuilderImple) MakeItems(items []string) { for _, item := range items { b.buf.WriteString(fmt.Sprintf("・%s\n", item)) } } func (b *textBuilderImple) Close() { // Do nothing } func (b *textBuilderImple) GetResult() string { return b.buf.String() }
HtmlBuilder
package builder import ( "bytes" "fmt" ) type htmlBuilderImple struct { buf bytes.Buffer } func NewHtmlBuilder() Builder { return &htmlBuilderImple{buf: bytes.Buffer{}} } func (b *htmlBuilderImple) MakeTitle(title string) { b.buf.WriteString(fmt.Sprintf("<html><head><title>%s</title></head><body>", title)) b.buf.WriteString(fmt.Sprintf("<h1>%s</h1>", title)) } func (b *htmlBuilderImple) MakeString(str string) { b.buf.WriteString(fmt.Sprintf("<p>%s</p>", str)) } func (b *htmlBuilderImple) MakeItems(items []string) { b.buf.WriteString("<ul>") for _, item := range items { b.buf.WriteString(fmt.Sprintf("<li>%s</li>", item)) } b.buf.WriteString("</ul>") } func (b *htmlBuilderImple) Close() { b.buf.WriteString("</body></html>") } func (b *htmlBuilderImple) GetResult() string { return b.buf.String() }
Client
ClientがBuilderを初期化してそれをDirectorに食わせる。
ClientではDirectorインターフェースのみ着目するため、DirectorとBuilderの具体的な処理は知らない。
package main import ( "fmt" "github.com/saekis/go-design-pattern/7_builder/director" "github.com/saekis/go-design-pattern/7_builder/builder" ) func main() { // Build text b := builder.NewTextBuilder() d := director.NewDocumentDirector(b) result := d.Build() fmt.Println(result) // Build html b = builder.NewHtmlBuilder() d = director.NewDocumentDirector(b) result = d.Build() fmt.Println(result) }
実行結果
$ go run main.go # text 「title」 string ・item1 ・item2 # html <html><head><title>title</title></head><body><h1>title</h1><p>string</p><ul><li>item1</li><li>item2</li></ul></body></html>