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

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

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

# ■ Golangで、Pythonクラスを実装し直す際の備忘録
**Golang では、Python等でお馴染みの、Inheritance（継承）ではなく、Composition（合成）のみが使われます。**

具体的、以下の２点を気をつけて、サンプルアプリの再実装に取り組みました。

- golangには、オブジェクトのクラス継承の概念が存在しないので、再利用性(Embedded)と多相性(Interface)の活用で対応する必要がある。 
- Pythonの場合だと、動的型付けのおかげて、引数／戻り値の型を意識することなくポリモーフィズムを実践できていたが、Golangの場合では、引数／戻り値の型を柔軟に扱えるようインタフェースを活用する必要がある。

## □ 大変、お世話になったQiita記事
- [Go言語で「embedded で継承ができる」と思わないほうがいいのはなぜか？](https://qiita.com/Maki-Daisuke/items/511b8989e528f7c70f80)
- [Goやるなら知っておきたい「Composition over inheritance」](https://qiita.com/kskinaba/items/903967dc8997e7d7444a)

# ■ Abstract Factory（アブストラクトファクトリ）とは？
Abstract Factory パターン（アブストラクト・ファクトリ・パターン）とは、GoF（Gang of Four; 4人のギャングたち）によって定義されたデザインパターンの1つである。 関連するインスタンス群を生成するための API を集約することによって、複数のモジュール群の再利用を効率化することを目的とする。日本語では「抽象的な工場」と翻訳される事が多い。Kit パターンとも呼ばれる

## □ UML class and sequence diagram
![W3sDesign_Abstract_Factory_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/c7a5008a-06ae-2fbc-5e17-5639b7073e38.jpeg)
## □ UML class diagram
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3130333539352f62393731323034392d616664392d323330332d653539372d3031393136333530633732382e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/944b8a7b-5d2a-093f-bbdb-15b06d6e4e38.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Abstract_Factory_パターン)


# ■ "Abstract Factory"のサンプルプログラム
Abstract Factoryパターンとは、**抽象的な工場**から、抽象的な部品を組み合わせて抽象的な製品を作るらしいです。
実際に、Abstract FactoryのPython実装コードを動かしてみて、**抽象的な工場**をイメージを理解したいと思います。ここで取り上げるサンプルプログラムは、階層構造を持ったリンク集をHTMLファイルとして作るものです。

- `ListFactory`モードで、動作させると、リスト形式のリンク集のHTMLファイルが生成されます。
- `TableFactory`モードで、動作させると、テーブル形式のリンク集のHTMLファイルが生成されます。

## (1) ListFactoryを動かしてみる
まずは、**Linkベース**のWebページを作成するコードを動かしてみます。

```console
$ go run Main.go ListFactory
[LinkPage.html] was created.
```
`LinkPage.html`というファイルが生成されました。
Webブラウザで、見た目を確認してみると、こんな感じでした。
![listfactory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/13ba9a4a-c09e-4815-6c6a-fb2a5d31ff9c.png)

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

```console
$ go run Main.go TableFactory
[LinkPage.html] was created.
```
`LinkPage.html`というファイルが生成されました。
Webブラウザで、見た目を確認してみると、こんな感じでした。
![tablefactory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/43c45b11-1941-f605-03c3-dcd27e1f99ad.png)

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

```console
.
├── Main.go
└── factory
    ├── factory.go
    ├── list_factory.go
    └── table_factory.go
```

## (1) AbstractProduct（抽象的な製品）の役
`AbstractProduct`役は、`AbstractFactory`役によって、作り出される抽象的な部品や製品のインタフェースを定めます。サンプルプログラムでは、`Link`構造体、`Tray`構造体、`Page`構造体と、各種インタフェースが、この役を努めます。

```go:factory.go
package factory

import (
	"fmt"
	"os"
)

...(snip)

// ItemInterface is interface
type ItemInterface interface {
	makeHTML() string
}

// Item is struct
type Item struct {
	caption string
}

// Link is struct
type Link struct {
	*Item
	url string
}

// TrayInterface is interface
type TrayInterface interface {
	ItemInterface
	Add(item ItemInterface)
}

// Tray is struct
type Tray struct {
	*Item
	tray []ItemInterface
}

// Add func for adding item into Tray
func (t *Tray) Add(item ItemInterface) {
	t.tray = append(t.tray, item)
}

// PageInterface is interface
type PageInterface interface {
	ItemInterface
	Add(item ItemInterface)
	Output(o ItemInterface)
}

// Page is struct
type Page struct {
	title, author string
	content       []ItemInterface
}

// Add func for adding item into Page
func (p *Page) Add(item ItemInterface) {
	p.content = append(p.content, item)
}

// Output func for outputing content
func (p *Page) Output(o ItemInterface) {
	filename := fmt.Sprintf("%s.html", p.title)
	file, _ := os.Create(filename)
	defer file.Close()
	b := []byte(o.makeHTML())
	file.Write(b)
	fmt.Printf("[%s] was created.\n", filename)
}
```
## (2) AbstractFactory（抽象的な工場）の役
`AbstractFactory`役は、`AbstractProduct`役のインスタンスを作り出すためのインタフェースを定めます。
サンプルプログラムでは、`Factory`インタフェースがこの役を勤めます。

```go:factory.go
package factory

import (
	"fmt"
	"os"
)

// Factory is struct
type Factory interface {
	CreateLink(caption, url string) LinkInterface
	CreateTray(caption string) TrayInterface
	CreatePage(title, author string) PageInterface
}

...(snip)
```
## (3) Client（依頼者）の役
`Client`役は、`AbstractFactory`役と`AbstractProduct`役のインタフェースだけを使って仕事を行います。
`Client`役は、具体的な部品や製品や工場については、知りません。なお、サンプルプログラムでは、`startMain `関数が、この役を努めます。

```go:Main.go
package main

import (
	"flag"

	"./factory"
)

func startMain(factoryObject factory.Factory) {
	asahi := factoryObject.CreateLink("Asahi", "http://www.asahi.com")
	yomiuri := factoryObject.CreateLink("Yomiuri", "http://www.yomiuri.co.jp")
	usYahoo := factoryObject.CreateLink("Yahoo", "http://www.yahoo.com")
	jaYahoo := factoryObject.CreateLink("Yahoo!Japan", "http://www.yahoo.co.jp")
	google := factoryObject.CreateLink("Google", "http://www.google.com")
	excite := factoryObject.CreateLink("Excite", "http://www.excite.co.jp")

	traynews := factoryObject.CreateTray("Newspaper")
	traynews.Add(asahi)
	traynews.Add(yomiuri)

	trayyahoo := factoryObject.CreateTray("Yahoo!")
	trayyahoo.Add(usYahoo)
	trayyahoo.Add(jaYahoo)

	traysearch := factoryObject.CreateTray("Search Engine")
	traysearch.Add(trayyahoo)
	traysearch.Add(excite)
	traysearch.Add(google)

	page := factoryObject.CreatePage("LinkPage", "Hiroshi Yuki")
	page.Add(traynews)
	page.Add(traysearch)
	page.Output(page)
}

func main() {
	flag.Parse()
	if flag.Arg(0) == "ListFactory" {
		startMain(&factory.ListFactory{})
	} else if flag.Arg(0) == "TableFactory" {
		startMain(&factory.TableFactory{})
	}
}
```

## (4) ConcreteProduct（具体的な製品）の役
`ConcreteProduct`役は、`AbstractProduct`役のインタフェースを実装します。サンプルプログラムでは、以下のクラスが、この役を努めます。

- `ListLink`構造体、`ListTray`構造体、`ListPage`構造体
- `TableLink`構造体、`TableTray`構造体、`TablePage`構造体

```go:list_factory.go
// ListLink is struct
type ListLink struct {
	*Link
}

func newListLink(caption, url string) *ListLink {
	return &ListLink{
		Link: &Link{
			Item: &Item{
				caption: caption,
			},
			url: url,
		},
	}
}

func (ll *ListLink) makeHTML() string {
	return fmt.Sprintf("  <li><a href=\"%s\">%s</a></li>\n", ll.url, ll.caption)
}

// ListTray is struct
type ListTray struct {
	*Tray
}

func newListTray(caption string) *ListTray {
	return &ListTray{
		Tray: &Tray{
			Item: &Item{
				caption: caption,
			},
		},
	}
}

func (lt *ListTray) makeHTML() string {
	buf := "<li>\n"
	buf += fmt.Sprintf("%s\n", lt.caption)
	buf += "<ul>\n"
	for _, item := range lt.tray {
		buf += item.makeHTML()
	}
	buf += "</ul>\n"
	buf += "</li>\n"
	return buf
}

// ListPage is struct
type ListPage struct {
	*Page
}

func newListPage(title, author string) *ListPage {
	return &ListPage{
		Page: &Page{
			title:  title,
			author: author,
		},
	}
}

func (lp *ListPage) makeHTML() string {
	buf := "<html>\n"
	buf += fmt.Sprintf("  <head><title>%s</title></head>\n", lp.title)
	buf += "<body>\n"
	buf += fmt.Sprintf("<h1>%s</h1>", lp.title)
	buf += "<ul>"
	for _, item := range lp.content {
		buf += item.makeHTML()
	}
	buf += "</ul>"
	buf += fmt.Sprintf("<hr><adress>%s</adress>", lp.author)
	buf += "</body>\n</html>\n"
	return buf
}
```
```go:table_factory.go
// TableLink is struct
type TableLink struct {
	*Link
}

func newTableLink(caption, url string) *TableLink {
	return &TableLink{
		Link: &Link{
			Item: &Item{
				caption: caption,
			},
			url: url,
		},
	}
}

func (tl *TableLink) makeHTML() string {
	return fmt.Sprintf("<td><a href=%s>%s</a></td>\n", tl.url, tl.caption)
}

// TableTray is struct
type TableTray struct {
	*Tray
}

func newTableTray(caption string) *TableTray {
	return &TableTray{
		Tray: &Tray{
			Item: &Item{
				caption: caption,
			},
		},
	}
}

func (tt *TableTray) makeHTML() string {
	buf := "<td>\n"
	buf += "<table width=\"100%\" border=\"1\"><tr>\n"
	buf += fmt.Sprintf("<td bgcolor=\"#cccccc\" algin=\"center\" colsapn=\"%d\"><b>%s</b></td>\n", len(tt.tray), tt.caption)
	buf += "</tr>\n"
	buf += "<tr>\n"
	for _, item := range tt.tray {
		buf += item.makeHTML()
	}
	buf += "</tr></table>\n"
	buf += "</td>\n"
	return buf
}

// TablePage is struct
type TablePage struct {
	*Page
}

func newTablePage(title, author string) *TablePage {
	return &TablePage{
		Page: &Page{
			title:  title,
			author: author,
		},
	}
}

func (tp *TablePage) makeHTML() string {
	buf := "<html>\n"
	buf += fmt.Sprintf("  <head><title>%s</title></head>\n", tp.title)
	buf += "<body>\n"
	buf += fmt.Sprintf("<h1>%s</h1>", tp.title)
	buf += "<table width=\"80%\" border=\"3\">\n"
	for _, item := range tp.content {
		buf += fmt.Sprintf("<tr>%s</tr>", item.makeHTML())
	}
	buf += "</table>"
	buf += fmt.Sprintf("<hr><adress>%s</adress>", tp.author)
	buf += "</body>\n</html>\n"
	return buf
}
```

## (5) ConcreteFactory（具体的な工場）の役
`ConcreteFactory`役は、`AbstractFactory`役のインタフェースを実装します。サンプルプログラムでは、以下の構造体が、この役を努めます。

- `ListFactory`構造体
- `TableFactory`構造体

```go:list_factory.go
// ListFactory is struct
type ListFactory struct {
}

// CreateLink func for creating Link
func (lf *ListFactory) CreateLink(caption, url string) ItemInterface {
	return newListLink(caption, url)
}

// CreateTray func for creating Tray
func (lf *ListFactory) CreateTray(caption string) TrayInterface {
	return newListTray(caption)
}

// CreatePage func for creating page
func (lf *ListFactory) CreatePage(title, author string) PageInterface {
	return newListPage(title, author)
}
```
```go:table_factory.go
// TableFactory is struct
type TableFactory struct {
}

// CreateLink func for creating Link
func (tf *TableFactory) CreateLink(caption, url string) ItemInterface {
	return newTableLink(caption, url)
}

// CreateTray func for creating Tray
func (tf *TableFactory) CreateTray(caption string) TrayInterface {
	return newTableTray(caption)
}

// CreatePage func for creating page
func (tf *TableFactory) CreatePage(title, author string) PageInterface {
	return newTablePage(title, author)
}
```

# ■ 終わりに...
Golangで、Pythonの継承クラスを実装し直してみると、**「Golang では、Python等でお馴染みの、Inheritance（継承）ではなく、Composition（合成）のみが使われる。」**という概念が、鮮明に理解できるようになりました。
この学びは、今度も、大切な経験になりそうです。


# ■ 参考URL
- [Pythonで、デザインパターン「Abstract Factory」を学ぶ](https://qiita.com/ttsubo/items/30c86a265430a9e5050a)
- [Go言語で「embedded で継承ができる」と思わないほうがいいのはなぜか？](https://qiita.com/Maki-Daisuke/items/511b8989e528f7c70f80)
- [Goやるなら知っておきたい「Composition over inheritance」](https://qiita.com/kskinaba/items/903967dc8997e7d7444a)