「State」という英単語は、「状態」を意味します。
このパターンでは、ある物についての各状態をそれに対応した各クラスで表現します。(つまり、状態1つにつき、クラス1つで表現します。)
通常、条件(状態)に一致するか否かの処理は、if文を使用し、コーディングしますが、その条件分岐数が多い場合、1つの条件で処理するコード量が多い場合、また同じ条件分岐が複数の箇所に点在する場合等、メンテナンスし辛いものとなってしまいます。しかし、このパターンを適用すると、その状態状態を個々のクラスで表現するため、単純明快となります。
状態を表すクラスです。
状態毎に振舞いが異なるメソッドのインタフェースを定義します。
「State」のインタフェースを実装し、具体的な状態を、「1クラス」 = 「1状態」 で定義します。
1つ状態を表すのに複数のオブジェクトは必要ないため、「Singleton」パターンを適用します。
現在の状態(「ConcreteStateA」か「ConcreteStateB」)を保持します。
利用者へのインタフェースを定義します。
状態を変更するメソッドを定義します。(状態の変更は、「ConcreteState」が次ぎの状態として相応しいものを判断し、この状態変更メソッドを呼出すことによって行います。)
Stateパターンのクラス図
Stateパターンのシーケンス図
1. State.java
public interface State {
public abstract void stateMethod1(Context context, int condition);
public abstract void stateMethod2(Context context);
}
2. ConcreteStateA.java
public class ConcreteStateA implements State {
private static final String stateName = "ConcreteStateA";
// -「Singleton」パターンを適用 -------------------------------------------
private static State concreteStateA = new ConcreteStateA();
private ConcreteStateA() {}
public static State getInstance() {
return concreteStateA;
}
// ------------------------------------------------------------------------
// 「Context」が参照しているアクティブな「State」オブジェクト変更用メソッド
public void stateMethod1(Context context, int condition) {
if (condition == 1) {
context.setState(ConcreteStateB.getInstance());
System.out.println("☆☆☆☆☆ 状態変更 A -> B ☆☆☆☆☆");
}
}
public void stateMethod2(Context context) {
context.contextMethod3(" 状態:" + stateName);
}
}
3. ConcreteStateB.java
public class ConcreteStateB implements State {
private static final String stateName = "ConcreteStateB";
// -「Singleton」パターンを適用 -------------------------------------------
private static State concreteStateB = new ConcreteStateB();
private ConcreteStateB() {}
public static State getInstance() {
return concreteStateB;
}
// ------------------------------------------------------------------------
// 「Context」が参照しているアクティブな「State」オブジェクト変更用メソッド
public void stateMethod1(Context context, int condition) {
if (condition == 0) {
context.setState(ConcreteStateA.getInstance());
System.out.println("★★★★★ 状態変更 B -> A ★★★★★");
}
}
public void stateMethod2(Context context) {
context.contextMethod3(" 状態:" + stateName);
}
}
4. Context.java
public class Context {
private State state = null;
public Context() {
state = ConcreteStateA.getInstance();
}
public void setState(State state) {
this.state = state;
}
public void contextMethod1(int condition) {
state.stateMethod1(this, condition);
}
public void contextMethod2() {
state.stateMethod2(this);
}
public void contextMethod3(String msg) {
System.out.println(msg);
}
}
5. Client.java
import java.util.Random;
public class Client {
public static void main(String[] args) {
Random rand = new Random();
Context context = new Context();
int temp = 0;
int condition = 0;
for (int i = 0; i < 10; i++) {
System.out.println("--------------------");
temp = rand.nextInt(10);
System.out.println(i + "回目:" + temp);
condition = temp % 2;
System.out.println(" :" + condition);
context.contextMethod1(condition);
context.contextMethod2();
System.out.println();
}
}
}
C:\sample\desin_pattern\state>javac Client.java [Enter] C:\sample\desin_pattern\state>java Client [Enter] -------------------- 0回目:3 :1 ☆☆☆☆☆ 状態変更 A -> B ☆☆☆☆☆ 状態:ConcreteStateB -------------------- 1回目:4 :0 ★★★★★ 状態変更 B -> A ★★★★★ 状態:ConcreteStateA -------------------- 2回目:4 :0 状態:ConcreteStateA -------------------- 3回目:7 :1 ☆☆☆☆☆ 状態変更 A -> B ☆☆☆☆☆ 状態:ConcreteStateB -------------------- 4回目:4 :0 ★★★★★ 状態変更 B -> A ★★★★★ 状態:ConcreteStateA -------------------- 5回目:6 :0 状態:ConcreteStateA -------------------- 6回目:4 :0 状態:ConcreteStateA -------------------- 7回目:7 :1 ☆☆☆☆☆ 状態変更 A -> B ☆☆☆☆☆ 状態:ConcreteStateB -------------------- 8回目:6 :0 ★★★★★ 状態変更 B -> A ★★★★★ 状態:ConcreteStateA -------------------- 9回目:4 :0 状態:ConcreteStateA