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

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

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

# ■ Observerパターン（オブザーバ・パターン）
Observerパターンとは、プログラム内のオブジェクトのイベント( 事象 )を他のオブジェクトへ通知する処理で使われるデザインパターンの一種。
通知するオブジェクト側が、通知されるオブジェクト側に観察（英: observe）される形になる事から、こう呼ばれる。
出版-購読型モデルとも呼ばれる。暗黙的呼び出しの原則と関係が深い。
分散イベント処理システムの実装にも使われる。言語によっては、このパターンで扱われる問題は言語が持つイベント処理構文で処理される。

## UML class and sequence diagram
![W3sDesign_Observer_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/363e09a2-e0cb-5463-cff3-246229bbfaa8.jpeg)
## UML class diagram
![2880px-Observer_w_update.svg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/86f40e9c-0509-be6e-f93e-0f51f9a061d2.png)

[(以上、ウィキペディア（Wikipedia）より引用)](https://en.wikipedia.org/wiki/Observer_pattern)

## □ 備忘録
`Observer`パターンでは、観察対象の状態が変化すると、観察者に対して通知されるので、状態変化に応じた処理を記述するときに有効だそうです。
`observer`という言葉の本来の意味は「観察者」ですが、実際には、`Observer`役は能動的に「観察」するのではなく、`Subject`役から「通知」されるのを受動的に待っていることになるので、`Publish-Subscribe`パターンと呼ばれることもあるそうです。
確かに、`publish（発行）`と`subscribe（購読）`という表現の方が、適切のような気がしました。

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

- 数をたくさん生成するオブジェクトを観察者が観察して、その値を表示する仕組みになります
- 表示の方法は、観察者によって異なります。
- `DigitalObserver`は、値を数字で表示します
- `GraphicObserver`は、値を簡易グラフで表示します



```console
$ go run Main.go 
DigitObservser: 10
GraphicObserver:**********
DigitObservser: 18
GraphicObserver:******************
DigitObservser: 14
GraphicObserver:**************
DigitObservser: 28
GraphicObserver:****************************
DigitObservser: 22
GraphicObserver:**********************
DigitObservser: 45
GraphicObserver:*********************************************
DigitObservser: 38
GraphicObserver:**************************************
DigitObservser: 37
GraphicObserver:*************************************
DigitObservser: 30
GraphicObserver:******************************
DigitObservser: 9
GraphicObserver:*********
DigitObservser: 9
GraphicObserver:*********
DigitObservser: 46
GraphicObserver:**********************************************
DigitObservser: 21
GraphicObserver:*********************
DigitObservser: 2
GraphicObserver:**
DigitObservser: 36
GraphicObserver:************************************
DigitObservser: 31
GraphicObserver:*******************************
DigitObservser: 4
GraphicObserver:****
DigitObservser: 27
GraphicObserver:***************************
DigitObservser: 33
GraphicObserver:*********************************
DigitObservser: 25
GraphicObserver:*************************
```

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

- ディレクトリ構成

```console
.
├── Main.go
└── observer
    ├── generator.go
    └── observer.go
```

## (1) Subject（被験者）の役
`Subject`役は、「観察される側」を表します。`Subject`役は、観察者である`Observer`役を登録するメソッドと、削除するメソッドを持っています。また、「現在の状態を取得する」メソッドも宣言されています。
サンプルプログラムでは、`NumberGenerator`構造体が、この役を努めます。

```go:observer/generator.go
package observer

import (
	"math/rand"
	"time"
)

// NumberGenerator is struct
type NumberGenerator struct {
	number    int
	observers []Observer
}

// AddObserver func for adding Observer
func (n *NumberGenerator) AddObserver(observer Observer) {
	n.observers = append(n.observers, observer)
}

func (n *NumberGenerator) notifyObserver() {
	for _, o := range n.observers {
		o.update(n)
	}
}

func (n *NumberGenerator) getNumber() int {
	return n.number
}
```
## (2) ConcreteSubject（具体的な被験者）の役
`ConcreteSubject`役は、具体的な「観察される側」を表現する役です。状態が変化したら、そのことを登録されている`Observer`役に伝えます。
サンプルプログラムでは、`RandomNumberGenerator`構造体が、この役を努めます。

```go:observer/generator.go
// RandomNumberGenerator is struct
type RandomNumberGenerator struct {
	*NumberGenerator
}

// NewRandomNumberGenerator func for initializing RandomNumberGenerator
func NewRandomNumberGenerator() *RandomNumberGenerator {
	return &RandomNumberGenerator{
		NumberGenerator: &NumberGenerator{number: 0},
	}
}

// Execute func for executing something
func (r *RandomNumberGenerator) Execute() {
	for i := 0; i < 20; i++ {
		rand.Seed(time.Now().UnixNano())
		r.number = rand.Intn(49)
		r.notifyObserver()
	}
}
```
## (3) Observer（観察者）の役
`Observer`役は、`Subject`役から「状態が変化しましたよ」と教えてもらう役です。そのためのメソッドが`update`です。
サンプルプログラムでは、`Observer`インタフェースが、この役を努めます。

```go:observer/observer.go
package observer

import (
	"fmt"
	"time"
)

// Observer is interface
type Observer interface {
	update(generator *NumberGenerator)
}
```
## (4) ConcreteObserver（具体的な観察者）の役
`ConcreteObserver`役は、具体的な`Observer`です。`update`メソッドが呼び出されると、そのメソッドの中で`Subject`役の現在の状態を取得します。
サンプルプログラムでは、`DigitObserver`構造体と`GraphObserver`構造体が、この役を努めます。

```go:observer/observer.go
// DigitObserver is struct
type DigitObserver struct {
}

// NewDigitObserver func for initializing DigitObserver
func NewDigitObserver() *DigitObserver {
	return &DigitObserver{}
}

func (d *DigitObserver) update(generator *NumberGenerator) {
	fmt.Printf("DigitObservser: %d\n", generator.getNumber())
	time.Sleep(time.Millisecond * 100)
}
```
```go:observer/observer.go
// GraphObserver is struct
type GraphObserver struct {
}

// NewGraphObserver func for initializing GraphObserver
func NewGraphObserver() *GraphObserver {
	return &GraphObserver{}
}

func (g *GraphObserver) update(generator *NumberGenerator) {
	fmt.Printf("GraphicObserver:")
	count := generator.getNumber()
	for i := 0; i < count; i++ {
		fmt.Printf("*")
	}
	fmt.Println("")
	time.Sleep(time.Millisecond * 100)
}
```
## (5) Client（依頼人）の役
サンプルプログラムでは、`startMain`関数が、この役を努めます。

```go:Main.go
package main

import (
	"./observer"
)

func startMain() {
	generator := observer.NewRandomNumberGenerator()
	observer1 := observer.NewDigitObserver()
	observer2 := observer.NewGraphObserver()
	generator.AddObserver(observer1)
	generator.AddObserver(observer2)
	generator.Execute()
}

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

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