AspectJ を使ったデザインパターンの改善と支援 >
State

asato <asato@ncfreak.com>

最終更新日 : 2002/9/26 (2002/9/26 より)

State パターン

オブジェクトの内部状態が変化したときに、オブジェクトが振る舞いを変えるようにする。クラス内では、振る舞いの変化を記述せず、状態を表すオブジェクトを導入することでこれを実現する。

拡張/変更がありえる部分

Context に対する新たな状態依存メソッドの追加

アスペクトを使わない場合の対策手順:
  1. Context に対しての新たな状態依存メソッドの定義(たとえば newRequest() メソッド)。
    • Context のソースコードの変更

  2. State に対しての、その newRequest() メソッドを扱うメソッドの定義 (たとえば newHandle() メソッド)。
    • State のソースコードの変更

  3. newRequest() メソッドの実装。state オブジェクトに対して 2 で定義した newHandle() メソッドを呼び出す。
    • Context のソースコードの変更

  4. 各 ConcreteState オブジェクトの newHandle() メソッドの実装。
    • 各 ConcreteState のソースコードの変更
アスペクトを使う場合の対策手順:
  1. 新しい状態依存メソッドに関するコードを扱うアスペクトの作成 (たとえば StateAspect)
    • StateAspect アスペクトの新規作成

  2. Introduction を使っての、Context に対して新たな状態依存メソッドの導入 (たとえば Context.newRequest() メソッド)
    • StateAspect アスペクトの編集

  3. Introduction を使っての、State に対して Context.newRequest() メソッドを扱う抽象メソッドの導入 (たとえば State.newHandle() メソッド)
    • StateAspect アスペクトの編集

  4. Introduction を使っての、各 ConcreteState に対して Context.newRequest() メソッドを扱うメソッドの導入 (たとえば ConcreteStateA.newHandle() メソッド)
    • StateAspect アスペクトの編集

改善となりえる部分

AspectJ を使った State に関するテクニックの種類

状態依存メソッドの局所化

意図: Context に対する新たな状態依存メソッドに関するコードを1つのアスペクト内に局所化する。


[Q] 1つのアスペクトにまとめる方が良いのか? それとも1つ以上のアスペクトに分割したほうがよい場合もありえるのか?

実装:

サンプルコード:

package dp.state;

public class Context {
	private State state;

	public void request() {
		state.handle();
	}
	public void setState(State state) {
		this.state = state;
	}
}
package dp.state;

public interface State {
	public void handle();
}
package dp.state;

public class ConcreteStateA implements State {
	public void handle() {
		System.out.println("ConcreteStateA - handle");
	}
}
package dp.state;

public class ConcreteStateB implements State {
	public void handle() {
		System.out.println("ConcreteStateB - handle");
	}
}
package dp;

import dp.state.*;

public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.setState( new ConcreteStateA() );
		context.request();
		context.newRequest();

		context.setState( new ConcreteStateB() );
		context.request();
		context.newRequest();
	}
}
package dp.state;

privileged aspect StateAspect {

	public void Context.newRequest() {
		this.state.newHandle();
	}

	public abstract void State.newHandle();

	public void ConcreteStateA.newHandle() {
		System.out.println("ConcreteStateA - newHandle");
	}
	public void ConcreteStateB.newHandle() {
		System.out.println("ConcreteStateB - newHandle");
	}
}

ConcreteStateA - handle
ConcreteStateA - newHandle
ConcreteStateB - handle
ConcreteStateB - newHandle

State パターンの導入

package dp.state;

public class Context {

	public static final int STATE_A = 0;
	public static final int STATE_B = 1;

	private int state = STATE_A;

	public void request() {
		if (state == STATE_A) {
			System.out.println("STATE A");

		} else if (state == STATE_B) {
			System.out.println("STATE B");
		}
	}
	public void setState(int state) {
		this.state = state;
	}
}

package dp;

import dp.state.*;

public class Client {

	public static void main(String[] args) {
		Context context = new Context();
		context.request();

		context.setState( Context.STATE_B );
		context.request();

		context.setState( Context.STATE_C );
		context.request();
	}
}
package dp.state;

import java.util.Map;
import java.util.HashMap;

public aspect StateAspect {

	private Map stateMap = new HashMap();

	public StateAspect() {
		stateMap.put( new Integer(Context.STATE_A), new ConcreteStateA() );
		stateMap.put( new Integer(Context.STATE_B), new ConcreteStateB() );
		stateMap.put( new Integer(Context.STATE_C), new ConcreteStateC() );
	}

	declare parents: ConcreteState* implements State;

	protected interface State { } 

	protected class ConcreteStateA { }
	protected class ConcreteStateB { }
	protected class ConcreteStateC { }

	public abstract void State.handle();

	public static final int Context.STATE_C = 2;

	private State Context.contextState;


	private Context State.context;

	private void State.setContext(Context context) {
		this.context = context;
	}

	public void (ConcreteStateA || ConcreteStateB).handle() {
		context.request();
	}
	public void ConcreteStateC.handle() {
		System.out.println("STATE C");
	}

	pointcut setState(Context context, int state) :
		this(context) && args(state) && set(int Context.state);

	after(Context context, int state) : setState(context, state) {
		context.contextState = (State)stateMap.get( new Integer(state) );
	}

	pointcut request(Context context) :
		!within(StateAspect) && target(context) && call( void Context.request() );

	void around(Context context) : request(context) {
		context.contextState.setContext(context);
		context.contextState.handle();
	}
}
STATE A
STATE B
STATE C