4
2

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

Last updated at Posted at 2023-11-02

Stateパターン

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

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

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

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

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

シチュエーション

エアコンで温度の状態「低」「中」「高」を管理するものとする。

Stateパターンを使用しない場合

状態をクライアント自身が管理している。

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

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

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が、現在の状態に基づく振る舞いを提供し、「状態」自身が次の遷移先の状態を知っている。

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

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

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

State.png

TemperatureState.java
// State
public interface TemperatureState {

    void increase();

    void decrease();
}
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());
    }
}
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());
    }
}
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("これ以上温度を下げることはできません。");
    }
}
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;
    }
}
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();
        // >> 温度を「低」に下げます。
        // >> これ以上温度を下げることはできません。
        // >> 温度を「中」に上げます。
        // >> 温度を「高」に上げます。
        // >> これ以上温度を上げることはできません。
        // >> 温度を「中」に下げます。
        // >> 温度を「低」に下げます。
    }
}

参考

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2