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

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

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

# ■ Flyweight（フライウェイト・パターン）
Flyweightパターン（フライウェイト・パターン）とは、GoFによって定義されたデザインパターンの1つである。 等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによってプログラムを省リソース化することを目的とする。

## UML class and sequence diagram
![W3sDesign_Flyweight_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/a4d6db2e-43cc-2944-b70e-f4ee25925eea.jpeg)
## UML class diagram
![flyweight.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/41164dc7-52bc-805e-1495-ed2df27186dd.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Flyweight_パターン)

## □ 備忘録
**flyweight**というのは、「フライ級」のことで、ボクシングで最も体重が軽い階級を示すものであり、このデザインパターンでは、オブジェクトを「軽く」するためのものだそうです。
`Flyweight`パターンで使っている技法は、「インスタンスをできるだけ共有させて、無駄にnewしない」というもので、インスタンスが必要なときに、いつもnewするのではなく、すでに作ってあるインスタンスを利用できるなら、それを共有して使うものだそうです。
直感的に、[デザインパターン「Singleton」](https://qiita.com/ttsubo/items/2498bf77f81b6ed23198)の応用だと感じますね。

# ■ "Flyweight"のサンプルプログラム

##(1) 事前準備
アクキーアートっぽく、数字を表示するテキストファイルを準備しておきます。

```text:big0.txt
....######......
..##......##....
..##......##....
..##......##....
..##......##....
..##......##....
....######......
................
```
```text:big1.txt
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
```
```text:big2.txt
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
```
（以下、略）
## (2) 動作確認
実際に、Flyweightパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

- 引数の数字の順番に、指定された数字をアスキーアートで表示する

```console
$ go run Main.go 012123
....######......
..##......##....
..##......##....
..##......##....
..##......##....
..##......##....
....######......
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
....######......
..##......##....
..........##....
......####......
..........##....
..##......##....
....######......
................
```

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

- ディレクトリ構成

```console
.
├── Main.go
├── big0.txt
├── big1.txt
├── big2.txt
├── big3.txt
├── big4.txt
├── big5.txt
├── big6.txt
├── big7.txt
├── big8.txt
├── big9.txt
└── flyweight
    └── big_char_factory.go
```
## (1) Flyweight（フライ級）の役
普通に扱うとプログラムが重くなるので共有した方が良いものを表す役です。
サンプルプログラムでは、`bigChar`構造体が、この役を努めます。

```go:flyweight/big_char_factory.go
package flyweight

import (
	"fmt"
	"os"
)

... (snip)

type bigChar struct {
	fontdata string
}

func newBigChar(charname string) *bigChar {
	char := &bigChar{}
	data := make([]byte, 256)
	f, err := os.Open(fmt.Sprintf("big%s.txt", charname))
	if err == nil {
		f.Read(data)
		char.fontdata = string(data)
	} else {
		char.fontdata = charname + "?"
	}
	return char
}

func (b *bigChar) print() {
	fmt.Println(b.fontdata)
}
```

## (2) FlyweightFactory（フライ級の工場）の役
`Flyweight`を作る工場の役です。この工場を使って`Flyweight`役を作ると、インスタンスが共有されます。
サンプルプログラムでは、`bigCharFactory`構造体が、この役を努めます。

```go:flyweight/big_char_factory.go
type bigCharFactory struct {
	pool map[string]*bigChar
}

func newBigCharFactory() *bigCharFactory {
	bigChrFct := &bigCharFactory{
		pool: make(map[string]*bigChar),
	}
	return bigChrFct
}

var instance *bigCharFactory

func getInstance() *bigCharFactory {
	if instance == nil {
		instance = newBigCharFactory()
	}
	return instance
}

func (b *bigCharFactory) getBigChar(charname string) *bigChar {
	var bc *bigChar
	if _, ok := b.pool[charname]; !ok {
		bc = newBigChar(charname)
		b.pool[charname] = bc
	} else {
		bc = b.pool[charname]
	}
	return bc
}
```
## (3) Client（依頼人）の役
`FlyweightFactory`役を使って`Flyweight`を作り出し、それを利用する役です。
サンプルプログラムでは、`BigString`構造体と`startMain`関数が、この役を努めます。

```go:flyweight/big_char_factory.go
// BigString is struct
type BigString struct {
	bigchars []*bigChar
}

// NewBigString func for initalizing BigString
func NewBigString(str string) *BigString {
	bigStr := &BigString{}
	factory := getInstance()
	for _, s := range str {
		bigStr.bigchars = append(bigStr.bigchars, factory.getBigChar(string(s)))
	}
	return bigStr
}

// Print func for print something
func (b *BigString) Print() {
	for _, bc := range b.bigchars {
		bc.print()
	}
}
```
```go:Main.go
package main

import (
	"flag"

	"./flyweight"
)

func startMain(str string) {
	bs := flyweight.NewBigString(str)
	bs.Print()
}

func main() {
	flag.Parse()
	startMain(flag.Arg(0))
}
```


# ■ 参考URL
- [Pythonで、デザインパターン「Flyweight」を学ぶ](https://qiita.com/ttsubo/items/2fecba5f66c7271128ab)