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

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

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

# ■ Composite（コンポジット・パターン）
Compositeパターン（コンポジット・パターン）とは、GoF (Gang of Four; 4人のギャングたち) によって定義された デザインパターンの1つである。「構造に関するパターン」に属する。Compositeパターンを用いるとディレクトリとファイルなどのような、木構造を伴う再帰的なデータ構造を表すことができる。
Composite パターンにおいて登場するオブジェクトは、「枝」と「葉」であり、これらは共通のインターフェースを実装している。そのため、枝と葉を同様に扱えるというメリットがある。

## UML class and object diagram
![W3sDesign_Composite_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/fb7706d4-cc53-eb2a-12c9-578d3d41b5e4.jpeg)
## UML class diagram
![composite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/2b5d3d28-4408-2d63-204f-9e33c813f50d.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Composite_パターン)

## □ 備忘録
書籍「増補改訂版Java言語で学ぶデザインパターン入門」の引用ですが、腹落ちしました。
>ディレクトリの中には、ファイルが入っていたり、別のディレクトリ（サブディレクトリ）が入っていたりします。そしてまた、そのサブディレクトリの中には他のファイルやサブディレクトリが入っていることもあります。
ディレクトリは、そのような「入れ子」になった構造、再帰的な構造を作り出しています。
... (snip)
Compositeパターンは、このような構造を作るためのものであり、**容器と中身を同一視し、再帰的な構造を作る**デザインパターン

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

- ルートエントリの`ディレクトリ`に、`サブディレクトリ`および`ファイル`を追加してみる
- ルートエントリの`ディレクトリ`に、ユーザエントリの`ディレクトリ`を追加して、さらに、 `サブディレクトリ`および`ファイル`を追加してみる
- 敢えて、`ファイル`に、`ディレクトリ`を追加して、失敗することを確認する

```console
$ go run Main.go 
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)

Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)

Occurring Exception...
FileTreatmentException
```

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

- ディレクトリ構成

```console
.
├── Main.go
└── composite
    ├── directory.go
    ├── entry.go
    └── file.go
```

## (1) Leaf（葉）の役
「中身」を表す役です。この役の中には、他のものを入れることができません。
サンプルプログラムでは、`File`構造体が、この役を努めます。

```go:composite/file.go
package composite

import (
	"fmt"
)

// File is sturct
type File struct {
	name string
	size int
}

// NewFile func for initializing File
func NewFile(name string, size int) *File {
	return &File{
		name: name,
		size: size,
	}
}

func (f *File) getName() string {
	return f.name
}

func (f *File) getSize() int {
	return f.size
}

// Add func for adding file
func (f *File) Add(entry entry) {
	if err := doError(); err != nil {
		fmt.Println(err)
	}
}

// PrintList func for printing directory name
func (f *File) PrintList(prefix string) {
	f.print(prefix)
}

func (f *File) print(prefix string) {
	fmt.Printf("%s/%s (%d)\n", prefix, f.getName(), f.getSize())
}

func doError() error {
	msg := "FileTreatmentException"
	return fmt.Errorf("%s", msg)
}
```

## (2) Composite（複合体）の役
「容器」を表す役です。`Leaf`役や、`Composite`役を入れることができます。
サンプルプログラムでは、`Directory`構造体が、この役を努めます。

```go:composite/directory.go
package composite

import "fmt"

// Directory is sturct
type Directory struct {
	name      string
	directory []entry
}

// NewDirectory func for initializing Directory
func NewDirectory(name string) *Directory {
	return &Directory{
		name: name,
	}
}

func (d *Directory) getName() string {
	return d.name
}

func (d *Directory) getSize() int {
	size := 0
	for _, entry := range d.directory {
		size += entry.getSize()
	}
	return size
}

// Add func for adding directory
func (d *Directory) Add(entry entry) {
	d.directory = append(d.directory, entry)
}

// PrintList func for printing directory name
func (d *Directory) PrintList(prefix string) {
	d.print(prefix)
	for _, e := range d.directory {
		e.PrintList(prefix + "/" + d.name)
	}
}

func (d *Directory) print(prefix string) {
	fmt.Printf("%s/%s (%d)\n", prefix, d.getName(), d.getSize())
}
```

## (3) Componentの役
`Leaf`役と`Composite`役を同一視するための役です。
サンプルプログラムでは、`Entry`インタフェースが、この役を努めます。

```go:composite/entry.go
package composite

type entry interface {
	PrintList(prefix string)
	getSize() int
}
```

## (4) Client（依頼人）の役
サンプルプログラムでは、`startMain`関数が、この役を努めます。

```go:Main.go
package main

import (
	"fmt"

	"./composite"
)

func startMain() {
	fmt.Println("Making root entries...")
	rootdir := composite.NewDirectory("root")
	bindir := composite.NewDirectory("bin")
	tmpdir := composite.NewDirectory("tmp")
	usrdir := composite.NewDirectory("usr")

	rootdir.Add(bindir)
	rootdir.Add(tmpdir)
	rootdir.Add(usrdir)

	bindir.Add(composite.NewFile("vi", 10000))
	bindir.Add(composite.NewFile("latex", 20000))
	rootdir.PrintList("")

	fmt.Println("")
	fmt.Println("Making user entries...")
	yuki := composite.NewDirectory("yuki")
	hanako := composite.NewDirectory("hanako")
	tomura := composite.NewDirectory("tomura")

	usrdir.Add(yuki)
	usrdir.Add(hanako)
	usrdir.Add(tomura)

	yuki.Add(composite.NewFile("diary.html", 100))
	yuki.Add(composite.NewFile("Composite.java", 200))
	hanako.Add(composite.NewFile("memo.tex", 300))
	tomura.Add(composite.NewFile("game.doc", 400))
	tomura.Add(composite.NewFile("junk.mail", 500))
	rootdir.PrintList("")

	fmt.Println("")
	fmt.Println("Occurring Exception...")
	tmpfile := composite.NewFile("junk.mail", 500)
	bindir = composite.NewDirectory("bin")
	tmpfile.Add(bindir)
}

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

## (5) その他
エラー時の振る舞いを追加します

```go:composite/file.go
func doError() error {
	msg := "FileTreatmentException"
	return fmt.Errorf("%s", msg)
}
```

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