Golangで、デザインパターン「Builder」を学ぶ

GoFのデザインパターンを学習する素材として、書籍「[増補改訂版Java言語で学ぶデザインパターン入門](https://www.hyuki.com/dp/)」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
[Qiita記事: "Pythonで、デザインパターン「Builder」を学ぶ"](https://qiita.com/ttsubo/items/0860e31c392aa3b91ed5)

今回は、Pythonで実装した” Builder”のサンプルアプリをGolangで実装し直してみました。

# ■ Builder（ビルダー）
Builderパターン（ビルダー・パターン）とは、GoF（Gang of Four; 4人のギャングたち）によって定義されたデザインパターンの1つである。 オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にする。
## UML class and sequence diagram
![W3sDesign_Builder_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/1d60984b-b8bf-fa00-96bc-a69e62a4c313.jpeg)
## UML class diagram
![2880px-Builder_UML_class_diagram.svg-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/f1a034f5-b5bf-6b6c-4c92-f73b7affcf1c.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Builder_パターン)


# ■ "Builder"のサンプルプログラム
Builderパターンとは、複雑な構造のものを一気に組み立てるのは難しいので、事前に全体を構成する各部分をつくって、段階を踏んで構造を持ったインスタンスを組み上げていくものらしいです。
実際に、Builderパターンを使って、「文書」を作成するサンプルプログラムを動かしてみて、動作の様子を確認したいと思います。

- `plain`モードで、動作させると、プレーンテキスト形式の文書が出力されます。
- `html`モードで、動作させると、テーブル形式のリンク集のHTMLファイルが生成されます。

## (1) `plain`モードで動かしてみる
まずは、プレーンテキスト形式の文書が出力するコードを動かしてみます。

```console
$ go run Main.go plain
======================
# Greeting

*** From the morning to the afternoon ***
- Good morning
- Hello
*** In the evening ***
- Good evening
- Good night
- Good bye
======================
```
所謂、プレーンテキストの文書が出力されました。

## (2) `html`モードを動かしてみる
つづいて、**Tableベース**のWebページを作成するコードを動かしてみます。

```console
$ go run Main.go html
[Greeting.html] was created.
```
`Greeting.html`というファイルが生成されました。
Webブラウザで、見た目を確認してみると、こんな感じでした。
![tablehtml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/2ed4902a-8b7e-4f32-ed21-ba81f7642d48.png)

# ■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Builder

- ディレクトリ構成

```console
.
├── Greeting.html
├── Main.go
└── builder
    ├── builder.go
    ├── director.go
    ├── html_builder.go
    └── text_builder.go
```

## (1) Builder（建築者）の役
`Builder`役は、インスタンスを作成するためのインタフェースを定めます。
`Builder`役には、インスタンスの各部分を作るためのメソッドが用意されます。
サンプルプログラムでは、`builder`インタフェースが、この役を努めます。

```go:builder/builder.go
package builder

type builder interface {
	makeTitle(title string)
	makeString(str string)
	makeItems(items []string)
	close()
}
```

## (2) ConcreteBuilder（具体的建築者）の役
`ConcreteBuilder`役は、`Builder`役のインタフェースを実装しているクラスです。実際のインスタンス作成で呼び出されるメソッドが、ここで定義されます。また、最終的にできた結果を得るためのメソッドが用意されます。
サンプルプログラムでは、`TextBuilder`構造体や、`HTMLBuilder`構造体が、この役を努めます。

```go:builder/text_builder.go
package builder

import "fmt"

// TextBuilder is struct
type TextBuilder struct {
	buffer string
}

// NewTextBuilder func for initializing TextBuilder
func NewTextBuilder() *TextBuilder {
	return &TextBuilder{}
}

func (t *TextBuilder) makeTitle(title string) {
	t.buffer += "======================\n"
	t.buffer += fmt.Sprintf("# %s\n", title)
	t.buffer += "\n"
}

func (t *TextBuilder) makeString(str string) {
	t.buffer += fmt.Sprintf("*** %s ***\n", str)
}

func (t *TextBuilder) makeItems(items []string) {
	for _, i := range items {
		t.buffer += fmt.Sprintf("- %s\n", i)
	}
}

func (t *TextBuilder) close() {
	t.buffer += "======================\n"
}

// GetResult func for fetching all from buffer
func (t *TextBuilder) GetResult() string {
	return t.buffer
}
```
```go:builder/html_builder.go
package builder

import (
	"fmt"
	"os"
)

// HTMLBuilder is struct
type HTMLBuilder struct {
	buffer, filename string
	f                *os.File
	makeTitleCalled  bool
}

// NewHTMLBuilder func for initializing HTMLBuilder
func NewHTMLBuilder() *HTMLBuilder {
	return &HTMLBuilder{}
}

func (h *HTMLBuilder) makeTitle(title string) {
	h.filename = fmt.Sprintf("%s.html", title)
	h.f, _ = os.Create(h.filename)
	h.f.Write([]byte(fmt.Sprintf("<html><head><title>%s</title></head></html>", title)))
	h.f.Write([]byte(fmt.Sprintf("<h1>%s</h1>", title)))
	h.makeTitleCalled = true
}

func (h *HTMLBuilder) makeString(str string) {
	if !h.makeTitleCalled {
		os.Exit(1)
	}
	h.f.Write([]byte(fmt.Sprintf("<p>%s</p>", str)))
}

func (h *HTMLBuilder) makeItems(items []string) {
	if !h.makeTitleCalled {
		os.Exit(1)
	}
	h.f.Write([]byte("<ul>"))
	for _, i := range items {
		h.f.Write([]byte(fmt.Sprintf("<li>%s</li>", i)))
	}
	h.f.Write([]byte("</ul>"))
}

func (h *HTMLBuilder) close() {
	if !h.makeTitleCalled {
		os.Exit(1)
	}
	h.f.Write([]byte("</body></html>"))
	h.f.Close()
}

// GetResult func for replying filename
func (h *HTMLBuilder) GetResult() string {
	return h.filename
}
```
## (3) Director（監督者）の役
`Director`役は、`Builder`役のインタフェースを使って、インスタンスを生成します。
`ConcreteBuilder`役に依存したプログラミングは行いません。`ConcreteBuilder`役が何であってもうまく動作するように、**`Builder`役のメソッドのみを使います**。
サンプルプログラムでは、`Director`タイプが、この役を努めます。

```go:builder/director.go
package builder

// Director is struct
type Director struct {
	builder builder
}

// NewDirector func for initializing Director
func NewDirector(builder builder) *Director {
	return &Director{
		builder: builder,
	}
}

// Construct func for conducting some methods of builder
func (d *Director) Construct() {
	d.builder.makeTitle("Greeting")
	d.builder.makeString("From the morning to the afternoon")
	d.builder.makeItems([]string{"Good morning", "Hello"})
	d.builder.makeString("In the evening")
	d.builder.makeItems([]string{"Good evening", "Good night", "Good bye"})
	d.builder.close()
}
```
## (4) Client（依頼人）の役
`Builder`役を利用する役です。
サンプルプログラムでは、`startMain`関数が、この役を努めます。

```go:Main.go
package main

import (
	"flag"
	"fmt"

	"./builder"
)

func startMain(opt string) {
	if opt == "plain" {
		builderObj := builder.NewTextBuilder()
		director := builder.NewDirector(builderObj)
		director.Construct()
		result := builderObj.GetResult()
		fmt.Println(result)
	} else if opt == "html" {
		builderObj := builder.NewHTMLBuilder()
		director := builder.NewDirector(builderObj)
		director.Construct()
		result := builderObj.GetResult()
		fmt.Printf("[%s] was created.\n", result)
	}
}

func main() {
	flag.Parse()
	startMain(flag.Arg(0))
}
```
# ■ 参考URL
- [Pythonで、デザインパターン「Abstract Factory」を学ぶ](https://qiita.com/ttsubo/items/30c86a265430a9e5050a)