「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