Design pattern - StatePatternを理解する

# はじめに
StatePatternについて説明します。

# StatePattern
StatePatternは次の課題を扱います。

- 状態が変化したときにobjectの振る舞いを変更する。
- すでにある状態の振る舞いを変更することなく、新しい状態を追加する。

State patternのstructureを示します。

![test-StatePattern1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/27d6490b-630d-1fcf-4203-2cdb328440d0.jpeg)

Contextは状態をあらわすstateをもちます。
Context:requestはstate.handle()を呼び出します。

IStateはStateのinterfaceを提供します。
State1 / State2 はIStateのinterfaceを実装します。

状態遷移の方法についてはSample Codeを参照ください。

# Sample Code1
次の状態遷移をするPlayerをState patternで実装します。

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/cdb19d01-0820-9258-a20b-302e34a3d0d8.png)

Playerは次の状態をもちます。

- Stopped
- Playing

play / stopのイベントにより状態が遷移します。
Playing状態に変化したとき、doPlayを実行します。
Stopped状態に変化したとき、doStopを実行します。

上記図に対応するPlantUMLは次の通りです。

```:plantuml
@startuml

[*] --> Stopped
Stopped --> Playing : play
Playing --> Stopped : stop

Stopped: entry / doStop
Playing: entry / doPlay

@enduml
```

Class図は以下の通りです。

![test-StatePattern2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/0792ef41-b68c-188c-23b3-dbd433a56e26.png)

Kotlinの実装は次の通りです。

```kotlin:test.kt
interface IPlayerState {
    fun enter(player: Player) {}
    fun play(player: Player) {}
    fun stop(player: Player) {}
}

class StateStopped : IPlayerState {
    override fun enter(player: Player) {
        doStop()
    }

    override fun play(player: Player) {
        println("play() : Stopped -> Playing")
        player.changeState(StatePlaying())
    }

    private fun doStop() = println("doStop()")
}

class StatePlaying : IPlayerState {
    override fun enter(player: Player) {
        doPlay()
    }

    override fun stop(player: Player) {
        println("stop() : Playing -> Stopped")
        player.changeState(StateStopped())
    }

    private fun doPlay() = println("doPlay()")
}

class Player {
    var state: IPlayerState = StateStopped()

    fun changeState(next: IPlayerState) {
        state = next
        state.enter(this)
    }

    fun play() {
        state.play(this)
    }

    fun stop() {
        state.stop(this)
    }
}

fun main() {
    val player = Player()
    player.play()
    player.stop()
    /*
        play() : Stopped -> Playing
        doPlay()
        stop() : Playing -> Stopped
        doStop()
     */
}
```

# Sample Code2
Sample Code1にPaused状態を追加します。
状態遷移は次の通りです。

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/37b98ea5-90c2-4edb-077a-502072e1d5e5.png)

上記図に対応するPlantUMLは次の通りです。

```:plantuml
@startuml

[*] --> Stopped
Stopped --> Playing : play
Playing --> Stopped : stop
Playing --> Paused : pause
Paused --> Playing : play
Paused --> Stopped : stop

Stopped: entry / doStop
Playing: entry / doPlay
Paused: entry / doPause

@enduml
```

Kotlinの実装は次の通りです。 
Sample Code1にたいして追加したところに "add" のコメントをつけます。

```kotlin:test.kt
interface IPlayerState {
    fun enter(player: Player) {}
    fun play(player: Player) {}
    fun stop(player: Player) {}
    fun pause(player: Player) {} // add
}

class StateStopped : IPlayerState {
    override fun enter(player: Player) {
        doStop()
    }

    override fun play(player: Player) {
        println("play() : Stopped -> Playing")
        player.changeState(StatePlaying())
    }

    private fun doStop() = println("doStop()")
}

class StatePlaying : IPlayerState {
    override fun enter(player: Player) {
        doPlay()
    }

    override fun stop(player: Player) {
        println("stop() : Playing -> Stopped")
        player.changeState(StateStopped())
    }

    // add start
    override fun pause(player: Player) {
        println("pause() : Playing -> Paused")
        player.changeState(StatePaused())
    }
    // add end

    private fun doPlay() = println("doPlay()")
}

// add start
class StatePaused : IPlayerState {
    override fun enter(player: Player) {
        doPause()
    }

    override fun play(player: Player) {
        println("play() : Paused  -> Playing")
        player.changeState(StatePlaying())
    }

    override fun stop(player: Player) {
        println("stop() : Paused  -> Stopped")
        player.changeState(StateStopped())
    }

    private fun doPause() = println("doPause()")
}
// add end

class Player {
    var state: IPlayerState = StateStopped()

    fun changeState(next: IPlayerState) {
        state = next
        state.enter(this)
    }

    fun play() {
        state.play(this)
    }

    fun stop() {
        state.stop(this)
    }

    // add start
    fun pause() {
        state.pause(this)
    }
    // add end
}

fun main() {
    val player = Player()
    player.play()
    player.pause() // add
    player.play()  // add
    player.stop()
    /*
        play() : Stopped -> Playing
        doPlay()
        pause() : Playing -> Paused
        doPause()
        play() : Paused  -> Playing
        doPlay()
        stop() : Playing -> Stopped
        doStop()
     */
}
```

# Sample Code3
Sample Code2をenumで書き換えてみます。

```kotlin:test.kt
class Player {
    enum class State {
        STOPPED {
            override fun enter(player: Player) {
                player.doStop()
            }

            override fun play(player: Player) {
                println("play() : Stopped -> Playing")
                player.changeState(PLAYING)
            }
        },
        PLAYING {
            override fun enter(player: Player) {
                player.doPlay()
            }

            override fun stop(player: Player) {
                println("stop() : Playing -> Stopped")
                player.changeState(STOPPED)
            }

            override fun pause(player: Player) {
                println("pause() : Playing -> Paused")
                player.changeState(PAUSED)
            }
        },
        PAUSED {
            override fun enter(player: Player) {
                player.doPause()
            }

            override fun play(player: Player) {
                println("play() : Paused -> Playing")
                player.changeState(PLAYING)
            }

            override fun stop(player: Player) {
                println("stop() : Paused -> Stopped")
                player.changeState(STOPPED)
            }
        };

        open fun enter(player: Player) {}
        open fun play(player: Player) {}
        open fun stop(player: Player) {}
        open fun pause(player: Player) {}
    }

    private var state: State = State.STOPPED

    fun changeState(next: State) {
        state = next
        state.enter(this)
    }

    fun play() {
        state.play(this)
    }

    fun doPlay() {
        println("doPlay()")
    }

    fun stop() {
        state.stop(this)
    }

    fun doStop() {
        println("doStop()")
    }

    fun pause() {
        state.pause(this)
    }

    fun doPause() {
        println("doPause()")
    }
}

fun main() {
    val player = Player()
    player.play()
    player.pause()
    player.play()
    player.stop()
    /*
        play() : Stopped -> Playing
        doPlay()
        pause() : Playing -> Paused
        doPause()
        play() : Paused -> Playing
        doPlay()
        stop() : Playing -> Stopped
        doStop()
     */
}
```