Stateパターン

# はじめに
GoFパターンの一つで,振る舞いに関するデザインパターンである
**Stateパターン**について見ていきます.

# Stateパターンとは
Stateパターンとは,**状態の変化に応じて振る舞いを変化させることができるパターン**です.

有限状態オートマトンと関連しており,Stateパターンを用いることで,
有限状態マシンを実装することができます.

# Stateパターンの構造
Stateパターンの構造は以下のようになります.
- Context : 内部の状態(State)を保ち,その状態に応じて異なる処理を委譲
- State : 状態固有のメソッドを宣言するインタフェース
- ConcreteState : 状態固有のメソッドに対して独自のメソッドを実装

クラス図は以下のようになります.
#### クラス図
![スクリーンショット 2025-01-17 163130.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586226/d177dfbb-1c98-d7aa-5578-1298589b9717.png)


# Stateパターンの実装
改札の閉じている状態と開いている状態をStateパターンで実装します.

以下がクラス図とソースコードとなります.

#### クラス図
![スクリーンショット 2025-01-17 163112.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586226/ec871ac8-570f-12e9-166f-790946b6e51f.png)



#### ソースコード
```java:GateState.java
public interface GateState {
    void insertTicket(Gate gate); // チケット挿入時の動作
    void passThrough(Gate gate);  // 通過時の動作
}
```

```java:LockedState.java
public class LockedState implements GateState {
    @Override
    public void insertTicket(Gate gate) {
        System.out.println("チケットが挿入されました。改札をアンロックします。");
        gate.setState(new UnlockedState());
    }

    @Override
    public void passThrough(Gate gate) {
        System.out.println("改札はロックされています。通過できません。");
    }
}
```

```java:UnlockedState.java
public class UnlockedState implements GateState {
    @Override
    public void insertTicket(Gate gate) {
        System.out.println("改札は既にアンロックされています。次に進んでください。");
    }

    @Override
    public void passThrough(Gate gate) {
        System.out.println("通過しました。改札をロックします。");
        gate.setState(new LockedState());
    }
}
```

```java:Gate.java
public class Gate {
    private GateState state;

    // 初期状態をロック状態に設定
    public Gate() {
        this.state = new LockedState();
    }

    // 状態を設定
    public void setState(GateState state) {
        this.state = state;
    }

    // 状態の操作を委譲
    public void insertTicket() {
        state.insertTicket(this);
    }

    public void passThrough() {
        state.passThrough(this);
    }
}
```

```java:GateStatePatternExample.java
public class GateStatePatternExample {
    public static void main(String[] args) {
        Gate gate = new Gate();

        gate.passThrough(); 

        gate.insertTicket(); 

        gate.passThrough(); 

        gate.passThrough(); 
    }
}
```

```:出力例
改札はロックされています。通過できません。
チケットが挿入されました。改札をアンロックします。
通過しました。改札をロックします。
改札はロックされています。通過できません。
```

各クラス概要は以下の通りです.

#### 1.GateState(State)
改札の状態に応じた共通のメソッドを定義.
(状態ごとに異なる動作を実装するための基となるクラス)

#### 2.LockedState(ConcreteState)
改札がロックされているときの状態を表すクラス.

チケットが挿入されると,UnlockedStateになります.

改札を通過しようとしても,状態は変わらず,通過できません.

#### 3.UnlockedState(ConcreteState)
改札がアンロックされているときの状態を表すクラス.

チケットが挿入されても,状態は変わりません.

改札を通過すると,LockedStateになります.

#### 4.Gate(Context)
改札の現在の状態を管理します.

# Stateパターンのメリット
Stateパターンを適用することによるメリットを見ていきます.

#### 状態の振る舞いと状態遷移の論理を分離
Stateパターンでは,状態ごとの振る舞いを個別のクラスとして定義します.

これにより,**状態ごとの振る舞いが独立して実装され,ほかの状態やコンテキストクラスに
影響を与えることがありません**.

そのため,新しい状態を追加したとしても,新たなConcreteStateを作成するだけでよく,
既存のConcreteStateなどに影響を与えることはありません.

また,状態遷移の論理についてはコンテキストクラスに集約させることで,
**状態遷移について変更したければコンテキストクラスにのみ注目**すればよいことになります.

このように,状態の振る舞いと状態遷移の論理を分離することで,
**拡張性の高い設計**を実現することができます.

# 参考
[アジャイルソフトウェア開発の教科書](https://www.amazon.co.jp/%E3%82%A2%E3%82%B8%E3%83%A3%E3%82%A4%E3%83%AB%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E9%96%8B%E7%99%BA%E3%81%AE%E5%A5%A5%E7%BE%A9-%E7%AC%AC2%E7%89%88-%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E9%96%8B%E7%99%BA%E3%81%AE%E7%A5%9E%E9%AB%84%E3%81%A8%E5%8C%A0%E3%81%AE%E6%8A%80-%E3%83%AD%E3%83%90%E3%83%BC%E3%83%88%E3%83%BBC%E3%83%BB%E3%83%9E%E3%83%BC%E3%83%81%E3%83%B3/dp/4797347783)