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

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

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

# ■ Bridge（ブリッジ・パターン）
Bridgeパターン（ブリッジ・パターン）とは、GoF（Gang of Four; 4人のギャングたち）によって定義されたデザインパターンの1つである。 「橋渡し」のクラスを用意することによって、クラスを複数の方向に拡張させることを目的とする。

## UML class and sequence diagram
![W3sDesign_Bridge_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/84fdedfa-a747-fa1f-454e-513817f72afc.jpeg)
## UML class diagram
![bridge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/1ae8a791-28d2-f93e-c3d6-3dcff1464760.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Bridge_パターン)

## □ 備忘録
Bridgeパターンは、**機能のクラス階層**と**実装のクラス階層**を橋渡しするらしいです。

### (1) 機能のクラス階層とは？
あるクラスに対して、新しい機能を追加したい場合、新しくサブクラスを定義した上で、メソッドを実装します。
既存のスーパークラスと、新たに定義したサブクラスの関係が、"機能のクラス階層"になります。
一般的には、以下のような関係を想定します。

- スーパークラスは基本的な機能を持っている
- サブクラスで新しい機能を追加する

### (2) 実装のクラス階層とは？
新しい実装を追加したい場合、抽象クラスから派生した具体的なサブクラスを定義した上で、メソッドを実装します。
既存の抽象クラスと、新たに派生した具体的なサブクラスの関係が、"実装のクラス階層"になります。
一般的には、以下のような関係を想定します。

- 抽象クラスでは、抽象メソッドによってインタフェースを規定している
- 派生したサブクラスは具象メソッドによってそのインタフェースを実装する

# ■ "Bridge"のサンプルプログラム
実際に、Bridgeパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
**機能のクラス階層**と**実装のクラス階層**を橋渡しを想定したサンプルになります。

- `DisplayFunc`と、`DisplayStringImpl`の橋渡しを経て、文字列を表示する
- `DisplayCountFunc`と、`DisplayStringImpl`の橋渡しを経て、文字列を表示する
- `DisplayCountFunc`と、`DisplayStringImpl`の橋渡しを経て、文字列を表示する
- `DisplayRandomFunc`と、`DisplayStringImpl`の橋渡しを経て、文字列を5回繰り返して表示する
- `DisplayRandomFunc`と、`DisplayStringImpl`の橋渡しを経て、文字列をランダム回数繰り返して表示する
- `DisplayFunc`と、`DisplayTextfileImpl`の橋渡しを経て、テキストファイルの内容を表示する

```console
$ go run Main.go 
+-----------+
|Hello Japan|
+-----------+

+-----------+
|Hello Japan|
+-----------+

+--------------+
|Hello Universe|
+--------------+

+--------------+
|Hello Universe|
|Hello Universe|
|Hello Universe|
|Hello Universe|
|Hello Universe|
+--------------+

+--------------+
|Hello Universe|
|Hello Universe|
+--------------+

aaa
bbb
ccc
ddd
eee
fff
ggg
```
サンプルプログラムを動かしただけだと、いまいち、何がしたいのかよく分かりませんね。
つづいて、サンプルプログラムの詳細を確認していきます。

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

- ディレクトリ構成

```console
.
├── Main.go
├── bridge
│   ├── display_count_func.go
│   ├── display_func.go
│   ├── display_impl.go
│   ├── display_random_func.go
│   ├── display_string_impl.go
│   └── display_textfile_impl.go
└── test.txt
```
## (1) Abstraction（抽象化）の役
`Implement`役のメソッドを使って、基本的な機能だけが実装されているクラスです。
サンプルプログラムでは、`DisplayFunc`構造体が、この役を努めます。

```go:bridge/display_func.go
package bridge

// DisplayFunc is struct
type DisplayFunc struct {
	impl displayImpl
}

// NewDisplayFunc func for initializing DisplayFunc
func NewDisplayFunc(impl displayImpl) *DisplayFunc {
	return &DisplayFunc{
		impl: impl,
	}
}

func (d *DisplayFunc) open() {
	d.impl.rawOpen()
}

func (d *DisplayFunc) printBody() {
	d.impl.rawPrint()
}

func (d *DisplayFunc) close() {
	d.impl.rawClose()
}

//Display  func for displaying string
func (d *DisplayFunc) Display() {
	d.open()
	d.printBody()
	d.close()
}
```
## (2) RefinedAbstraction（改善した抽象化）の役
`Abstraction`役に対して機能を追加した役です。
サンプルプログラムでは、`DisplayCountFunc`構造体と、`DisplayRandomFunc`構造体が、この役を努めます。

```go:bridge/display_count_func.go
package bridge

// DisplayCountFunc is struct
type DisplayCountFunc struct {
	*DisplayFunc
}

// NewDisplayCountFunc func for initializing DisplayCountFunc
func NewDisplayCountFunc(impl displayImpl) *DisplayCountFunc {
	return &DisplayCountFunc{
		DisplayFunc: &DisplayFunc{
			impl: impl,
		},
	}
}

//MultiDisplay  func for displaying string
func (d *DisplayCountFunc) MultiDisplay(times int) {
	d.open()
	for i := 0; i < times; i++ {
		d.printBody()
	}
	d.close()
}
```
```go:bridge/display_random_func.go
package bridge

import (
	"math/rand"
	"time"
)

// DisplayRandomFunc is struct
type DisplayRandomFunc struct {
	*DisplayFunc
}

// NewDisplayRandomFunc func for initializing DisplayRandomFunc
func NewDisplayRandomFunc(impl displayImpl) *DisplayRandomFunc {
	return &DisplayRandomFunc{
		DisplayFunc: &DisplayFunc{
			impl: impl,
		},
	}
}

//RandomDisplay  func for displaying string
func (d *DisplayRandomFunc) RandomDisplay(times int) {
	d.open()
	rand.Seed(time.Now().UnixNano())
	randomTimes := rand.Intn(times)
	for i := 0; i < randomTimes; i++ {
		d.printBody()
	}
	d.close()
}
```
## (3) Implementor（実装者）の役
`Abstraction`役のインタフェースを実装するためのメソッドを規定する役です。
サンプルプログラムでは、`DisplayImpl`インタフェースが、この役を努めます。

```go:bridge/display_impl.go
package bridge

type displayImpl interface {
	rawOpen()
	rawPrint()
	rawClose()
}
```
## (4) ConcreteImplementor（具体的な実装者）の役
具体的に`Implement`役のインタフェースを実装する役です。
サンプルプログラムでは、`DisplayStringImpl`構造体と、`DisplayTextfileImpl`構造体が、この役を努めます。

```go:bridge/display_string_impl.go
package bridge

import (
	"fmt"
	"strings"
)

// DisplayStringImpl is struct
type DisplayStringImpl struct {
	str   string
	width int
}

// NewDisplayStringImpl func for initializing DisplayStringImpl
func NewDisplayStringImpl(str string) *DisplayStringImpl {
	return &DisplayStringImpl{
		str:   str,
		width: len(str),
	}
}

func (d *DisplayStringImpl) rawOpen() {
	d.printLine()
}

func (d *DisplayStringImpl) rawPrint() {
	fmt.Printf("|%s|\n", d.str)
}

func (d *DisplayStringImpl) rawClose() {
	d.printLine()
	fmt.Println("")
}

func (d *DisplayStringImpl) printLine() {
	line := strings.Repeat("-", d.width)
	fmt.Printf("+%s+\n", line)
}
```
```go:bridge/display_textfile_impl.go
package bridge

import (
	"fmt"
	"os"
)

// DisplayTextfileImpl is struct
type DisplayTextfileImpl struct {
	fileName string
	f        *os.File
}

// NewDisplayTextfileImpl func for initializing DisplayTextfileImpl
func NewDisplayTextfileImpl(str string) *DisplayTextfileImpl {
	return &DisplayTextfileImpl{
		fileName: str,
	}
}

func (d *DisplayTextfileImpl) rawOpen() {
	fp, err := os.Open(d.fileName)
	if err != nil {
		panic(err)
	} else {
		d.f = fp
	}
}

func (d *DisplayTextfileImpl) rawPrint() {
	buf := make([]byte, 64)
	_, err := d.f.Read(buf)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", buf)
}

func (d *DisplayTextfileImpl) rawClose() {
	d.f.Close()
}
```
## (5) Client（依頼人）の役
サンプルプログラムでは、`startMain`関数が、この役を努めます。

```go:Main.go
package main

import (
	"./bridge"
)

func startMain() {
	d1 := bridge.NewDisplayFunc(bridge.NewDisplayStringImpl("Hello Japan"))
	d2 := bridge.NewDisplayCountFunc(bridge.NewDisplayStringImpl("Hello Japan"))
	d3 := bridge.NewDisplayCountFunc(bridge.NewDisplayStringImpl("Hello Universe"))
	d4 := bridge.NewDisplayRandomFunc(bridge.NewDisplayStringImpl("Hello Universe"))
	d5 := bridge.NewDisplayFunc(bridge.NewDisplayTextfileImpl("test.txt"))
	d1.Display()
	d2.Display()
	d3.Display()
	d3.MultiDisplay(5)
	d4.RandomDisplay(5)
	d5.Display()
}

func main() {
	startMain()
}
```


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