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

GoFのデザインパターンをGolangで学習してみたいと思います。
今回は、Qiita記事: "[Pythonで、デザインパターン「State」を学ぶ](https://qiita.com/ttsubo/items/cb3219a520451840f23f)"で取り上げた、Pythonベースの”State”のサンプルアプリをGolangで実装し直してみました。

# ■ State（ステート・パターン）
Stateパターンとは、プログラミングで用いられる振る舞いに関する（英語版） デザインパターンの一種である。このパターンはオブジェクトの状態（state）を表現するために用いられる。ランタイムでそのタイプを部分的に変化させるオブジェクトを扱うクリーンな手段となる。

## UML class and sequence diagram
![W3sDesign_State_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/2e2b5e97-53c1-09b9-bcc2-c47602ffc501.jpeg)
## UML class diagram
![state.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/6bc61e44-7564-20ec-6cb9-1700314667da.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://en.wikipedia.org/wiki/State_pattern)

## □ 備忘録
`State`パターンでは、「状態」という物をクラスで表現するそうです。
状態に依存した振る舞いをここの`ConcreteState`役に分担させることが可能になります。
ただ、`State`パターンを使う場合、**状態遷移を誰が管理すべきか**という点には注意が必要です。
（状態遷移を`ConcreteState`役に任せてしまうとクラス間の依存関係を深めてしまいます。）

# ■ "State"のサンプルプログラム
実際に、Stateパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。ここでは、<font color="blue">"パソコン操作による起動状態の可視化"</font>をイメージしてください。

- パソコンを起動すると、動作状態は、"running"になる
- パソコンを停止すると、動作状態が、"shutdown"になる
- パソコンを再起動すると、動作状態が、"running"になる


なお、サンプルプログラムでは、**第一引数：最初のパソコン操作**、**第二引数：二度目のパソコン操作**を指定します。

## (事例1) パソコンを起動して、停止する
動作状態は、最初、パソコンに起動よって、"running"になって、その後、パソコンの停止によって、"shutdown"になります。

```console
$ go run Main.go start stop
### パソコンを、[start]します
*** パソコンは、起動中です
### パソコンは、[running]の動作状態になりました

... sleep 5 second

### パソコンを、[stop]します
*** パソコンは、停止しています
### パソコンの動作状態は、[shutdown]になりました
```

## (事例2) パソコンを起動して、再起動する
動作状態は、最初、パソコンに起動よって、"running"になって、その後、パソコンの再起動によって、再び、"running"になります。

```console
$ go run Main.go start restart
### パソコンを、[start]します
*** パソコンは、起動中です
### パソコンは、[running]の動作状態になりました

... sleep 5 second

### パソコンを、[restart]します
*** パソコンは、再起動をはじめます
*** パソコンは、起動中です
### パソコンの動作状態は、[running]になりました
```
以上で、想定どおり、サンプリプログラムが動作しました。

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

- ディレクトリ構成

```console
.
├── Main.go
└── state
    ├── context.go
    └── state.go
```
## (1) State（状態）の役
`State`役は、状態を表すためのものです。状態ごとに異なる振る舞いをするインタフェースを定めます。
サンプルプログラムでは、`State`インタフェースが、この役を努めます。

```go:state/state.go
package state

import "fmt"

// State is interface
type State interface {
	handle(context *Context)
	getConcreateState() string
}
```
## (2) ConcreteState（具体的な状態）の役
`ConcreteState`役は、具体的な個々の状態を表現するものです。
`State`役で定められたインタフェースを具体的に実装します。
サンプルプログラムでは、

- `ConcreteStateBooting`構造体
- `ConcreteStateRun`構造体
- `ConcreteStateShutDown`構造体
- `ConcreteStateRestart`構造体

が、この役を努めます。

```go:state/state.go
type concreteState struct {
	state string
}

func (c *concreteState) getConcreateState() string {
	return c.state
}
```
```go:state/state.go
// ConcreteStateBooting is struct
type ConcreteStateBooting struct {
	*concreteState
}

// NewConcreteStateBooting func for initializing ConcreteStateBooting
func NewConcreteStateBooting(state string) *ConcreteStateBooting {
	return &ConcreteStateBooting{
		concreteState: &concreteState{
			state: state,
		},
	}
}

func (c *ConcreteStateBooting) handle(context *Context) {
	fmt.Println("*** パソコンは、起動中です")
	context.SetState(NewConcreteStateRun("running"))
}
```
```go:state/state.go
// ConcreteStateRun is struct
type ConcreteStateRun struct {
	*concreteState
}

// NewConcreteStateRun func for initializing ConcreteStateRun
func NewConcreteStateRun(state string) *ConcreteStateRun {
	return &ConcreteStateRun{
		concreteState: &concreteState{
			state: state,
		},
	}
}

func (c *ConcreteStateRun) handle(context *Context) {
	fmt.Println("*** パソコンは、動作中です")
}
```
```go:state/state.go
// ConcreteStateShutDown is struct
type ConcreteStateShutDown struct {
	*concreteState
}

// NewConcreteStateShutDown func for initializing ConcreteStateShutDown
func NewConcreteStateShutDown(state string) *ConcreteStateShutDown {
	return &ConcreteStateShutDown{
		concreteState: &concreteState{
			state: state,
		},
	}
}

func (c *ConcreteStateShutDown) handle(context *Context) {
	fmt.Println("*** パソコンは、停止しています")
}
```
```go:state/state.go
// ConcreteStateRestart is struct
type ConcreteStateRestart struct {
	*concreteState
}

// NewConcreteStateRestart func for initializing ConcreteStateRestart
func NewConcreteStateRestart(state string) *ConcreteStateRestart {
	return &ConcreteStateRestart{
		concreteState: &concreteState{
			state: state,
		},
	}
}

func (c *ConcreteStateRestart) handle(context *Context) {
	fmt.Println("*** パソコンは、再起動をはじめます")
	context.SetState(NewConcreteStateBooting("booting"))
	context.Handle()
}
```
## (3) Context（状態、前後関係、文脈）の役
`Context`役は、現在の状態を表す`ConcreteState`役のオブジェクトを保持します。
サンプルプログラムでは、`Context`構造体が、この役を努めます。

```go:state/context.go
package state

// Context is struct
type Context struct {
	state State
}

// NewContext func for initializing Context
func NewContext(stateObj State) *Context {
	return &Context{
		state: stateObj,
	}
}

// SetState func for change state
func (c *Context) SetState(obj State) {
	c.state = obj
}

// Handle func for handling state
func (c *Context) Handle() {
	c.state.handle(c)
}

// GetState func for fetching state
func (c *Context) GetState() string {
	return c.state.getConcreateState()
}
```
## (4) Client（依頼人）の役
サンプルプログラムでは、`startMain`関数が、この役を努めます。

```go:Main.go
package main

import (
	"flag"
	"fmt"
	"time"

	"./state"
)

func setConcreteState(operation string) state.State {
	var stateObj state.State

	if operation == "start" {
		stateObj = state.NewConcreteStateBooting("booting")
	} else if operation == "stop" {
		stateObj = state.NewConcreteStateShutDown("shutdown")
	} else if operation == "restart" {
		stateObj = state.NewConcreteStateRestart("restart")
	}
	return stateObj
}

func startMain(initialOperation, changeOperation string) {
	obj := state.NewContext(setConcreteState(initialOperation))
	fmt.Printf("### パソコンを、[%s]します\n", initialOperation)
	obj.Handle()
	fmt.Printf("### パソコンは、[%s]の動作状態になりました\n", obj.GetState())
	fmt.Println("")

	fmt.Println("... sleep 5 second")
	fmt.Println("")
	time.Sleep(time.Second * 5)
	obj.SetState(setConcreteState(changeOperation))
	fmt.Printf("### パソコンを、[%s]します\n", changeOperation)
	obj.Handle()
	fmt.Printf("### パソコンの動作状態は、[%s]になりました\n", obj.GetState())
}

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

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