デザインパターン　Stateパターン

# Stateパターン
オブジェクト指向プログラミングでは、人や物をオブジェクトとして表現する。

Stateパターンでは、**「状態」をオブジェクトで表現する**。

状態は`State`クラスとして表現され、`Context`クラスが保持し、その状態に基づく振る舞いをする。

状態遷移（次にどの状態へなるか）は、各`State`オブジェクト自身が知っている（設計方法により、`Context`オブジェクトが管理する場合もある）。

Stateパターンは、オブジェクトの振る舞いがその「状態」に依存し、状態が変化することで振る舞いも変わる場合に有用。このパターンを使用すると、状態に基づく条件文（if-elseなど）を減少させ、コードの保守性と拡張性を向上させることができる。

## シチュエーション
エアコンで温度の状態「低」「中」「高」を管理するものとする。

## Stateパターンを使用しない場合
状態をクライアント自身が管理している。

状態が増えるたびにクライアント側のコードの修正が必要。

状態の追加や変更に対して柔軟性が低い。

```java:Main.java
// クライアント
public class Main {
    // 0：低　　1：中　　2：高
    static int state = 1;

    static void increase() {
        if (state == 0) {
            System.out.println("温度を「中」に上げます。");
            state++;
        } else if (state == 1) {
            System.out.println("温度を「高」に上げます。");
            state++;
        } else if (state == 2) {
            System.out.println("これ以上温度を上げることはできません。");
        }
    }

    static void decrease() {
        if (state == 0) {
            System.out.println("これ以上温度を下げることはできません。");
        } else if (state == 1) {
            System.out.println("温度を「低」に下げます。");
            state--;
        } else if (state == 2) {
            System.out.println("温度を「中」に下げます。");
            state--;
        }
    }

    public static void main(String[] args) {
        decrease();
        decrease();
        increase();
        increase();
        increase();
        decrease();
        decrease();
        // >> 温度を「低」に下げます。
        // >> これ以上温度を下げることはできません。
        // >> 温度を「中」に上げます。
        // >> 温度を「高」に上げます。
        // >> これ以上温度を上げることはできません。
        // >> 温度を「中」に下げます。
        // >> 温度を「低」に下げます。
    }
}

```

## Stateパターンを使用した場合
**クライアントは状態の管理に関わらない**。

`Context`が、現在の状態に基づく振る舞いを提供し、「状態」自身が次の遷移先の状態を知っている。

:::note info
Stateパターンでは、「状態」自身が次の遷移先を知っている。
:::

新しい状態の追加や既存の状態の変更が局所的な変更で済む。

これにより、「クライアント」と「状態」間の結合が疎になる。

![State.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/75598411-f023-509c-2b45-805f8bd819ed.png)

```java:TemperatureState.java
// State
public interface TemperatureState {

    void increase();

    void decrease();
}
```

```java:HighTemperatureState.java
// ConcreteState
public class HighTemperatureState implements TemperatureState {

    // Context
    AirConditioner airConditioner;

    public HighTemperatureState(AirConditioner airConditioner) {
        this.airConditioner = airConditioner;
    }

    @Override
    public void increase() {
        System.out.println("これ以上温度を上げることはできません。");
    }

    @Override
    public void decrease() {
        System.out.println("温度を「中」に下げます。");
        // 次に遷移する状態を「状態」自身が知っている
        airConditioner.setState(airConditioner.getMiddleState());
    }
}
```

```java:MiddleTemperatureState.java
// ConcreteState
public class MiddleTemperatureState implements TemperatureState {

    // Context
    AirConditioner airConditioner;

    public MiddleTemperatureState(AirConditioner airConditioner) {
        this.airConditioner = airConditioner;
    }

    @Override
    public void increase() {
        System.out.println("温度を「高」に上げます。");
        // 次に遷移する状態を「状態」自身が知っている
        airConditioner.setState(airConditioner.getHighState());
    }

    @Override
    public void decrease() {
        System.out.println("温度を「低」に下げます。");
        // 次に遷移する状態を「状態」自身が知っている
        airConditioner.setState(airConditioner.getLowState());
    }
}
```

```java:LowTemperatureState.java
// ConcreteState
public class LowTemperatureState implements TemperatureState{

    // Context
    AirConditioner airConditioner;

    public LowTemperatureState(AirConditioner airConditioner){
        this.airConditioner = airConditioner;
    }

    @Override
    public void increase() {
        System.out.println("温度を「中」に上げます。");
        // 次に遷移する状態を「状態」自身が知っている
        airConditioner.setState(airConditioner.getMiddleState());
    }

    @Override
    public void decrease() {
        System.out.println("これ以上温度を下げることはできません。");
    }
}
```

```java:AirConditioner.java
// Context
public class AirConditioner {

    private TemperatureState highState;
    private TemperatureState middleState;
    private TemperatureState lowState;

    TemperatureState state;

    public AirConditioner() {
        highState = new HighTemperatureState(this);
        middleState = new MiddleTemperatureState(this);
        lowState = new LowTemperatureState(this);

        state = middleState;
    }

    public void increaseTemperature() {
        state.increase();
    }

    public void decreaseTemperature() {
        state.decrease();
    }

    void setState(TemperatureState state) {
        this.state = state;
    }

    TemperatureState getHighState() {
        return highState;
    }

    TemperatureState getMiddleState() {
        return middleState;
    }

    TemperatureState getLowState() {
        return lowState;
    }
}
```

```java:Main.java
// クライアント
public class Main {
    public static void main(String[] args) {
        AirConditioner airConditioner = new AirConditioner();
        
        airConditioner.decreaseTemperature();
        airConditioner.decreaseTemperature();
        airConditioner.increaseTemperature();
        airConditioner.increaseTemperature();
        airConditioner.increaseTemperature();
        airConditioner.decreaseTemperature();
        airConditioner.decreaseTemperature();
        // >> 温度を「低」に下げます。
        // >> これ以上温度を下げることはできません。
        // >> 温度を「中」に上げます。
        // >> 温度を「高」に上げます。
        // >> これ以上温度を上げることはできません。
        // >> 温度を「中」に下げます。
        // >> 温度を「低」に下げます。
    }
}
```

# 参考
・<a href="https://amzn.asia/d/8MCJXIB" target="_blank" rel="noopener noreferrer">Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本</a>