状態パターン(State Pattern)は、オブジェクトの内部状態に応じて振る舞いを変更するデザインパターンです。このパターンでは、状態ごとに処理を分離して管理することで、複雑な条件分岐を回避し、コードの可読性と拡張性を高めます。
適用ケース
状態パターンは、オブジェクトの状態遷移が複雑になり、if-elseやswitch文の条件分岐が肥大化しがちな場合に特に有効です。状態ごとに専用のクラスを定義することで、状態変化に伴う振る舞いの変更をローカルに閉じ込め、保守性を向上させます。
実装例:ガムボールマシン
以下は、ガムボールマシンの状態遷移を状態パターンで実装した例です。マシンは「投幣待ち」「投幣済み」「販売中」「在庫切れ」の4つの状態を持ち、それぞれの状態で異なる振る舞いをします。
1. 状態インターフェース
package com.example.state;
public interface MachineState {
void insertCoin();
void returnCoin();
void turnHandle();
void dispense();
}
2. 投幣待ち状態
package com.example.state;
public class WaitingForCoinState implements MachineState {
private GumballMachine machine;
public WaitingForCoinState(GumballMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("コインを確認しました");
machine.changeState(machine.getHasCoinState());
}
@Override
public void returnCoin() {
System.out.println("コインを返却できません。まだ投入されていません");
}
@Override
public void turnHandle() {
System.out.println("ハンドルを回しましたが、コインがありません");
}
@Override
public void dispense() {
System.out.println("まずコインを支払ってください");
}
@Override
public String toString() {
return "コイン待ち";
}
}
3. 投幣済み状態
package com.example.state;
public class HasCoinState implements MachineState {
private GumballMachine machine;
public HasCoinState(GumballMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("すでにコインを挿入しています");
}
@Override
public void returnCoin() {
System.out.println("コインを返却しました");
machine.changeState(machine.getNoCoinState());
}
@Override
public void turnHandle() {
System.out.println("ハンドルを回しました");
machine.changeState(machine.getSoldState());
}
@Override
public void dispense() {
System.out.println("ガムボールを排出できません");
}
@Override
public String toString() {
return "ハンドル待ち";
}
}
4. 在庫切れ状態
package com.example.state;
public class SoldOutState implements MachineState {
private GumballMachine machine;
public SoldOutState(GumballMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("在庫切れです。コインを受け付けられません");
}
@Override
public void returnCoin() {
System.out.println("コインを返却できません。まだ投入されていません");
}
@Override
public void turnHandle() {
System.out.println("ハンドルを回しましたが、ガムボールがありません");
}
@Override
public void dispense() {
System.out.println("排出するガムボールがありません");
}
@Override
public String toString() {
return "在庫切れ";
}
}
5. 販売中状態
package com.example.state;
public class SoldState implements MachineState {
private GumballMachine machine;
public SoldState(GumballMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("現在ガムボールを排出中です");
}
@Override
public void returnCoin() {
System.out.println("すでにハンドルを回しました。返金できません");
}
@Override
public void turnHandle() {
System.out.println("もう一度回してもガムボールは出ません");
}
@Override
public void dispense() {
machine.releaseBall();
if (machine.getCount() > 0) {
machine.changeState(machine.getNoCoinState());
} else {
System.out.println("在庫がなくなりました!");
machine.changeState(machine.getSoldOutState());
}
}
@Override
public String toString() {
return "ガムボール排出中";
}
}
6. 状態を管理するマシンクラス
package com.example.state;
public class GumballMachine {
private MachineState noCoinState;
private MachineState hasCoinState;
private MachineState soldState;
private MachineState soldOutState;
private MachineState currentState;
private int ballCount;
public GumballMachine(int count) {
this.noCoinState = new WaitingForCoinState(this);
this.hasCoinState = new HasCoinState(this);
this.soldState = new SoldState(this);
this.soldOutState = new SoldOutState(this);
this.ballCount = count;
this.currentState = (count > 0) ? noCoinState : soldOutState;
}
public void insertCoin() {
currentState.insertCoin();
}
public void returnCoin() {
currentState.returnCoin();
}
public void turnHandle() {
currentState.turnHandle();
currentState.dispense();
}
void changeState(MachineState state) {
this.currentState = state;
}
void releaseBall() {
System.out.println("ガムボールが排出されました");
if (ballCount > 0) {
ballCount--;
}
}
public int getCount() {
return ballCount;
}
public MachineState getNoCoinState() {
return noCoinState;
}
public MachineState getHasCoinState() {
return hasCoinState;
}
public MachineState getSoldState() {
return soldState;
}
public MachineState getSoldOutState() {
return soldOutState;
}
}
7. 動作確認用テストクラス
package com.example.state;
public class GumballMachineTest {
public static void main(String[] args) {
GumballMachine machine = new GumballMachine(3);
System.out.println(machine);
machine.insertCoin();
machine.turnHandle();
System.out.println(machine);
machine.insertCoin();
machine.turnHandle();
System.out.println(machine);
machine.insertCoin();
machine.turnHandle();
System.out.println(machine);
}
}