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

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

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

# ■ Decorator（デコレータ・パターン）
Decoratorパターン（デコレータ・パターン）とは、GoF（Gang of Four; 4人のギャングたち）によって定義されたデザインパターンの1つである。 このパターンは、既存のオブジェクトに新しい機能や振る舞いを動的に追加することを可能にする。

## UML class and sequence diagram
![W3sDesign_Decorator_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/69b7e55e-6e1d-6c3f-0cc8-bae1553cf69e.jpeg)
## UML class diagram
![decorator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/c0384c43-968d-50e2-0809-6b361d808341.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Decorator_パターン)

## □ 備忘録
オブジェクトにどんどんデコレーション（飾り付け）を施していくようなデザインパターンとのことです。
スポンジケーキに、クリームを塗り、イチゴを載せればストロベリーショートケーキになるような感じです。
Pythonプログラミングに携わっていると、よくお目にかかるやつですね。

# ■ "Decorator"のサンプルプログラム
実際に、Decoratorパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

- 文字列`Hello, world.`をそのまま表示する
- 文字列`Hello, world.`の前後に、`#`文字を差し込んで表示する
- 文字列`Hello, world.`の前後に、`#`文字を差し込んで、さらに、 枠線で囲んで表示する
- 文字列`HELLO`を 枠線で囲んで、前後に、`*`文字を差し込んで、さらに、 枠線で２回囲んで、、、（以下、略）


```console
$ go run Main.go 
Hello, world.
#Hello, world.#
+---------------+
|#Hello, world.#|
+---------------+

/+-----------+/
/|+---------+|/
/||*+-----+*||/
/||*|HELLO|*||/
/||*+-----+*||/
/|+---------+|/
/+-----------+/
```

見た目、ハノイの塔みたいな表示になりました。

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

- ディレクトリ構成

```console
.
├── Main.go
└── decorator
    ├── border.go
    └── display.go
```
## (1) Componentの役
機能を追加するときの核に役です。`Component`役は。スポンジケーキのインタフェースだけを定めます。
サンプルプログラムでは、`display`構造体と、`displayInterface`インタフェースが、この役を努めます。

```go:decorator/display.go
package decorator

import "fmt"

type displayInterface interface {
	getColumns() int
	getRows() int
	getRowText(row int) string
}

type display struct {
	myDisplay displayInterface
}

// Show func for printing something
func (d *display) Show() {
	str := ""
	for i := 0; i < d.myDisplay.getRows(); i++ {
		str += d.myDisplay.getRowText(i) + "\n"
	}
	fmt.Printf("%s", str)
}

... (snip)
```
## (2) ConcreteComponentの役
`Component`役のインタフェースを実装している具体的なスポンジケーキです。
サンプルプログラムでは、`StringDisplay`構造体が、この役を努めます。

```go:decorator/display.go
// StringDisplay is struct
type StringDisplay struct {
	*display
	String string
}

// NewStringDisplay func for initalizing StringDisplay
func NewStringDisplay(str string) *StringDisplay {
	stringDisplay := &StringDisplay{
		display: &display{},
		String:  str,
	}
	stringDisplay.myDisplay = stringDisplay
	return stringDisplay
}

func (s *StringDisplay) getColumns() int {
	return len(s.String)
}

func (s *StringDisplay) getRows() int {
	return 1
}

func (s *StringDisplay) getRowText(row int) string {
	if row == 0 {
		return s.String
	}
	return ""
}
```

## (3) Decorator（装飾者）の役
`Component`役と同じインタフェースを持ちます。そして、さらに、この`Decorator`役が飾る対象となる`Component`役を持っています。この役は、自分が飾っている対象を「知っている」わけです。
サンプルプログラムでは、`border`構造体が、この役を努めます。

```go:decorator/border.go
package decorator

type border struct {
	*display
	neighorDisplay displayInterface
}

... (snip)
```
## (4) ConcreteDecorator（具体的な装飾者）の役
具体的な`Decorator`の役です。
サンプルプログラムでは、`SideBorder`構造体と、`FullBorder`構造体が、この役を努めます。

```go:decorator/border.go
// SideBorder is struct
type SideBorder struct {
	*border
	borderChar string
}

// NewSideBorder func for initalizing SideBorder
func NewSideBorder(displayIf displayInterface, borderChar string) *SideBorder {
	sideBorder := &SideBorder{
		border: &border{
			display:        &display{},
			neighorDisplay: displayIf,
		},
		borderChar: borderChar,
	}
	sideBorder.myDisplay = sideBorder
	return sideBorder
}

func (s *SideBorder) getColumns() int {
	return len(s.borderChar)*2 + s.neighorDisplay.getColumns()
}

func (s *SideBorder) getRows() int {
	return s.neighorDisplay.getRows()
}

func (s *SideBorder) getRowText(row int) string {
	return s.borderChar + s.neighorDisplay.getRowText(row) + s.borderChar
}
```
```go:decorator/border.go
// FullBorder is struct
type FullBorder struct {
	*SideBorder
}

// NewFullBorder func for initalizing SideBorder
func NewFullBorder(displayIf displayInterface) *FullBorder {
	fullBorder := &FullBorder{
		SideBorder: &SideBorder{
			border: &border{
				display:        &display{},
				neighorDisplay: displayIf}},
	}
	fullBorder.myDisplay = fullBorder
	return fullBorder
}

func (f *FullBorder) getColumns() int {
	return 2 + f.neighorDisplay.getColumns()
}

func (f *FullBorder) getRows() int {
	return 2 + f.neighorDisplay.getRows()
}

func (f *FullBorder) getRowText(row int) string {
	if row == 0 {
		return "+" + f.makeLine("-", f.neighorDisplay.getColumns()) + "+"
	} else if row == f.neighorDisplay.getRows()+1 {
		return "+" + f.makeLine("-", f.neighorDisplay.getColumns()) + "+"
	} else {
		return "|" + f.neighorDisplay.getRowText(row-1) + "|"
	}
}

func (f *FullBorder) makeLine(char string, count int) string {
	buf := ""
	for i := 0; i < count; i++ {
		buf += char
	}
	return buf
}
```
## (5) Client（依頼人）の役
サンプルプログラムでは、`startMain`関数が、この役を努めます。

```go:Main.go
package main

import (
	"fmt"

	"./decorator"
)

func startMain() {
	b1 := decorator.NewStringDisplay("Hello, world.")
	b2 := decorator.NewSideBorder(b1, "#")
	b3 := decorator.NewFullBorder(b2)
	b1.Show()
	b2.Show()
	b3.Show()
	fmt.Println("")
	b4 := decorator.NewSideBorder(
		decorator.NewFullBorder(
			decorator.NewFullBorder(
				decorator.NewSideBorder(
					decorator.NewFullBorder(
						decorator.NewStringDisplay("HELLO"),
					),
					"*",
				),
			),
		),
		"/",
	)
	b4.Show()
}

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

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