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

asato <asato@ncfreak.com>

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

Strategy パターン

アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする。Strategy パターンを利用することで、アルゴリズムを、それを利用するクライアントからは独立に変更することができる。

AspectJ による Strategy パターンの実装

実装 1
実装 2
実装 3
実装 4
実装 5
実装 6
実装 7
実装 8
実装 9

考慮点

まえがき

Strategy はすでにほとんどシンプルな構造になっていると考える。Strategy に関わる構成要素は以下のものである: それらの関係は:
	          使う                      保持
	Client --------------> Context ----------------> Strategy
	   |                     │                  +->
	   |                     │                  |
	   |                     ×                  | 実装
	   |  生成               │                  |
	   +-----------------> ConcreteStrategy -----+
	
必要なコードは:

感想

Strategy で AOP が威力を発揮しそうな部分はなさそうに思える・・・

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

力ずく AspectJ 的 Strategy

意図: 複雑になろうとも意地でも AspectJ を使って Strategy を実装する。

結果:

サンプルコード:

package dp.strategy;

public class Context {
	public void contextInterface() { }
}
package dp.strategy;

public class ConcreteStrategyA {
	public void method() {
		System.out.println("ConcreteStrategyA");
	}
}
package dp.strategy;

public class ConcreteStrategyB {
	public void perform() {
		System.out.println("ConcreteStrategyB");
	}
}
package dp;

import dp.strategy.*;

public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();
	}

	private static aspect ClientAspect extends StrategyAspect {

		pointcut newContext() :
			withincode( void  main(String[]) ) && call( Context.new() );

		Context around() : newContext() {
			Context context = new Context();
			setStrategy( context, new ConcreteStrategyB() );

			return context;
		}
	}
}
package dp.strategy;

public abstract aspect StrategyProtocol {

	protected interface Strategy { }
	protected interface StrategyContext { }

	private Strategy StrategyContext.strategy;

	private void StrategyContext.setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}
	private Strategy StrategyContext.getStrategy() {
		return strategy;
	}

	protected void setStrategy(StrategyContext context, Strategy strategy) {
		context.setStrategy(strategy);
	}
	protected Strategy getStrategy(StrategyContext context) {
		return context.strategy;
	}

	protected abstract pointcut contextInterface(StrategyContext context);

	void around(StrategyContext context) : contextInterface(context) {
		perform( context.getStrategy() );
	}
	
	protected pointcut perform() : call(void perform(Strategy) );

	private void perform(Strategy strategy) { }
}
package dp.strategy;

public abstract aspect StrategyAspect extends StrategyProtocol {

	declare parents : Context implements StrategyContext;

	declare parents : ConcreteStrategyA implements Strategy;
	declare parents : ConcreteStrategyB implements Strategy;

	protected pointcut contextInterface(StrategyContext context) :
		target(context) && call( void Context.contextInterface() );

	void around(ConcreteStrategyA str) : args(str) && perform() {
		perform(str);
	}

	void around(ConcreteStrategyB str) : args(str) && perform() {
		perform(str);
	}

	private void perform(ConcreteStrategyA strategy) {
		strategy.method();
	}
	private void perform(ConcreteStrategyB strategy) {
		strategy.perform();
	}
}
ConcreteStrategyB

考慮すべき要求:

実装:
  1. アルゴリズムに必要なデータへのアクセス:
  2. ConcreteStrategy はどのようにして Context クラスへアクセスするのか:
  3. クライアントに対して ConcreteStrategy オブジェクトを Context に直接渡すためのインタフェースを定義するのかどうか:
起こりうる変化と適応:

Strategy の導入

意図: ソースコードを変更することなく Strategy を導入する。
package dp.strategy;

public class Context {

	public static final int STRATEGY_A = 0;
	public static final int STRATEGY_B = 1;

	private int strategy;

	public Context(int strategy) {
		this.strategy = strategy;
	}

	public void contextInterface() {
		switch (strategy) {
			case STRATEGY_A :
				strategyA();
				break;

			case STRATEGY_B :
				strategyB();
				break;
		}
	}
	private void strategyA() {
		System.out.println("StrategyA");
	}
	private void strategyB() {
		System.out.println("StrategyB");
	}
}
package dp.strategy;

public class MyStrategy {
	public void algorithmInterface() {
		System.out.println("My Strategy");
	}
}
package dp;

import dp.strategy.*;

public class Client {

	public static void main(String[] args) {

		Context context = new Context( Context.STRATEGY_A );
		context.contextInterface();

		context.setStrategy( new MyStrategy() );
		context.contextInterface();

		context.setStrategy( Context.STRATEGY_B );
		context.contextInterface();
	}
}
package dp.strategy;

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

privileged aspect StrategyAspect {

	private static Map strategyMap = new HashMap();

	static {
		strategyMap.put( new Integer(Context.STRATEGY_A), new OriginalStrategy() );
		strategyMap.put( new Integer(Context.STRATEGY_B), new OriginalStrategy() );
	}

	private static Strategy getOriginalStrategy(int strategy) {
		return (Strategy)strategyMap.get( new Integer(strategy) );
	}

	declare parents: MyStrategy implements Strategy;
	declare parents: OriginalStrategy implements Strategy;

	private static class OriginalStrategy {

		public void algorithmInterface() {
			context.contextInterface();
		}
	}

	protected interface Strategy { }

	public abstract void Strategy.algorithmInterface();

	public void Strategy.setContext(Context context) {
		this.context = context;
	}

	private Context Strategy.context;

	private Strategy Context.strategyObject;

	public void Context.setStrategy(Strategy strategy) {
		this.strategyObject = strategy;
	}
	public void Context.setStrategy(int strategy) {
		this.strategy = strategy;
	}

	private Strategy Context.getStrategy() {
		return strategyObject;
	}

	pointcut contextInterface(Context context) :
		!within(StrategyAspect) &&
		target(context) &&
		call( void Context.contextInterface() );

	void around(Context context) : contextInterface(context) {
		context.getStrategy().algorithmInterface();
	}
	
	pointcut setOriginalStrategy(Context context, int strategy) :
		target(context) && args(strategy) && set(int Context.strategy);

	after(Context context, int strategy) : setOriginalStrategy(context, strategy) {
		Strategy strat = getOriginalStrategy( strategy );
		strat.setContext( context );
		context.setStrategy( strat );
	}
}
StrategyA
My Strategy
StrategyB

実装:

実装 - その 2

(1.1rc2 OK)

段階 - その 1

public class Context {

	private Strategy strategy;

	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}
	
	public void contextInterface() {
		strategy.algorithmInterface();
	}
}
public interface Strategy {

	public void algorithmInterface();
}
public aspect ConcreteStrategyA implements Strategy perthis( this(Context) ) {

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyA");
	}
	
	public static ConcreteStrategyA getInstance(Context context) {
		return ConcreteStrategyA.aspectOf(context);
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.setStrategy( ConcreteStrategyA.getInstance(context) );

		context.contextInterface(); // ConcreteStrategyA
	}
}

段階 - その 2

public class Context {

	private Strategy strategy;

	private String state;

	public Context(String state) {
		this.state = state;
	}

	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}
	
	public void contextInterface() {
		strategy.algorithmInterface();
	}
}
privileged aspect ConcreteStrategyA implements Strategy perthis( this(Context) ) {

	private Context context;

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyA - " + context.state);
	}
	
	public static ConcreteStrategyA getInstance(Context context) {

		ConcreteStrategyA strategy = ConcreteStrategyA.aspectOf(context);
		strategy.context = context;

		return strategy;
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context("aaa");
		context.setStrategy( ConcreteStrategyA.getInstance(context) );

		context.contextInterface(); // ConcreteStrategyA - aaa
	}
}

実装 - その 3 [2]

(1.1 OK)

[2] を元に少しの修正を加えています。

public class Context {
	public void contextInterface() { }
}
public aspect Strategy pertarget( method(Context) ) {
	
	private pointcut method(Context context) :
		target(context) && call(void Context.contextInterface() );

	public static final int STRATEGY_1 = 1;
	public static final int STRATEGY_2 = 2;
	
	private int Context.strategy = 0;
	
	public Context.new(int strategy) {
		this.strategy = strategy;
	}
	
	
	void around(Context context) : method(context) {

		if (context.strategy == STRATEGY_1) {
			strategy1();
		} else if (context.strategy == STRATEGY_2) {
			strategy2();
		} else {
			defaultStrategy();
		}
	}

	private void strategy1() {
		System.out.println("strategy1");
	}
	private void strategy2() {
		System.out.println("strategy2");
	}

	public void defaultStrategy() {
		System.out.println("defaultStrategy");
	}
}
public class Client {

	public static void main(String[] args) {
		Context context = new Context(Strategy.STRATEGY_1);
		context.contextInterface(); // "strategy1"
	}
}

実装 - その 4

段階 - その 1

(1.1.1 OK)

public class Context {
	public void contextInterface() {
		System.out.println("Context.contextInterface()");
	}
}
public aspect StrategyAspect pertarget( target(Context) ) {

	private boolean isActive;

	public void activate() {
		this.isActive = true;
	}

	void around() : call( void Context.contextInterface() ) {

		if (isActive) {
			algorithmInterface();

		} else {
			proceed();
		}
	}

	private void algorithmInterface() {
		System.out.println("StrategyAspect.algorithmInterface()");
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();


		StrategyAspect.aspectOf(context).activate();

		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
StrategyAspect.algorithmInterface()

段階 - その 2 : 複数の strategy が必要になったら?

(1.1.1 OK)

public class Context {
	public void contextInterface() {
		System.out.println("Context.contextInterface()");
	}
}
public abstract aspect AbstractStrategyAspect pertarget( target(Context) ) {

	private boolean isActive;
	private Context context;
	private AbstractStrategyAspect Context.strategy;

	public void activate() {
		this.isActive = true;
		if (context.strategy != null) {
			context.strategy.deactivate();
		}
		context.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	after(Context context): target(context) && initialization( Context.new() ) {
		this.context = context;
	}

	void around() : call( void Context.contextInterface() ) {

		if (isActive) {
			algorithmInterface();

		} else {
			proceed();
		}
	}
	
	protected abstract void algorithmInterface();
}
public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void algorithmInterface() {
		System.out.println("StrategyAspectA.algorithmInterface()");
	}
}
public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void algorithmInterface() {
		System.out.println("StrategyAspectB.algorithmInterface()");
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();


		StrategyAspectA.aspectOf(context).activate();
		context.contextInterface();


		StrategyAspectB.aspectOf(context).activate();
		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
StrategyAspectA.algorithmInterface()
StrategyAspectB.algorithmInterface()

段階 - その 3 : 再利用可能にできるか?

(1.1.1 OK)

public class MyContext {

	public void contextInterface() {
		System.out.println("MyContext.contextInterface()");
	}
}
public abstract aspect StrategyProtocol pertarget( target(Context) ) {

	protected interface Context { }

	private boolean isActive;
	private Context context;
	private StrategyProtocol Context.strategy;

	public void activate() {
		this.isActive = true;
		if (context.strategy != null) {
			context.strategy.deactivate();
		}
		context.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	after(Context context): target(context) && initialization( Context.new() ) {
		this.context = context;
	}

	protected abstract pointcut callContextInterface();

	void around() : callContextInterface() {

		if (isActive) {
			algorithmInterface();

		} else {
			proceed();
		}
	}
	
	protected abstract void algorithmInterface();
}
public abstract aspect AbstractStrategyAspect extends StrategyProtocol {

	declare parents : MyContext implements Context;

	protected pointcut callContextInterface() :
		call(void MyContext.contextInterface() );

	protected void algorithmInterface() {
		myAlgorithmInterface();
	}

	protected abstract void myAlgorithmInterface();
}
public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectA.myAlgorithmInterface()");
	}
}
public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectB.myAlgorithmInterface()");
	}
}
public class Client {

	public static void main(String[] args) {

		MyContext context = new MyContext();
		context.contextInterface();


		StrategyAspectA.aspectOf(context).activate();
		context.contextInterface();


		StrategyAspectB.aspectOf(context).activate();
		context.contextInterface();
	}
}
実行結果:
MyContext.contextInterface()
StrategyAspectA.myAlgorithmInterface()
StrategyAspectB.myAlgorithmInterface()

バイトコード・ウィービングシナリオ

(1.1.1 OK)

ライブラリ側 (lib パッケージ):

public class MyContext {
	public void contextInterface() {
		System.out.println("MyContext.contextInterface()");
	}
}
アプリケーション側 (app パッケージ):
public abstract aspect StrategyProtocol pertarget( target(Context) ) {

	protected interface Context { }

	private boolean isActive;
	private Context context;
	private StrategyProtocol Context.strategy;

	public void activate() {
		this.isActive = true;
		if (context.strategy != null) {
			context.strategy.deactivate();
		}
		context.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	after(Context context): target(context) && initialization( Context.new() ) {
		this.context = context;
	}

	protected abstract pointcut callContextInterface();

	void around() : callContextInterface() {

		if (isActive) {
			algorithmInterface();

		} else {
			proceed();
		}
	}
	
	protected abstract void algorithmInterface();
}
import lib.MyContext;

public abstract aspect AbstractStrategyAspect extends StrategyProtocol {

	declare parents : MyContext implements Context;

	protected pointcut callContextInterface() :
		call(void MyContext.contextInterface() );

	protected void algorithmInterface() {
		myAlgorithmInterface();
	}
	protected abstract void myAlgorithmInterface();
}
public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectA.myAlgorithmInterface()");
	}
}
public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectB.myAlgorithmInterface()");
	}
}
import lib.MyContext;

public class Client {

	public static void main(String[] args) {

		MyContext context = new MyContext();
		context.contextInterface();


		StrategyAspectA.aspectOf(context).activate();
		context.contextInterface();


		StrategyAspectB.aspectOf(context).activate();
		context.contextInterface();
	}
}
実行結果:
MyContext.contextInterface()
StrategyAspectA.myAlgorithmInterface()
StrategyAspectB.myAlgorithmInterface()

実装 - その 5

段階 - その 1

public class Context {
	public void contextInterface() {
		System.out.println("Context.contextInterface()");
	}
}
public aspect StrategyAspect {

	private Map roleMap = new HashMap();

	public void apply(Context context) {

		ContextRole role = null;

		if ( roleMap.containsKey(context) == false ) {

			role = new ContextRole();
			roleMap.put(context,  role);

		} else {
			role = (ContextRole)roleMap.get(context);
		}

		Strategy.aspectOf(role).activate();
	}
	
	private ContextRole getRole(Context context) {
		return (ContextRole)roleMap.get(context);
	}
	
	private static class ContextRole { }

	private static aspect Strategy pertarget( target(ContextRole) || target(Context) ) {

		private boolean isActive;
	
		public void activate() {
			this.isActive = true;
		}

	
		void around(Context context) :
			target(context) &&
			call( void Context.contextInterface() ) &&
			if (StrategyAspect.aspectOf().getRole(context) != null)
		{
			ContextRole role = StrategyAspect.aspectOf().getRole(context);
			if ( Strategy.aspectOf( role ).isActive ) {
				algorithmInterface();
			} else {
				proceed(context);
			}
		}
		
		public void algorithmInterface() {
			System.out.println("Strategy.algorithmInterface()");
		}

	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();

		StrategyAspect.aspectOf().apply(context);
		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
Strategy.algorithmInterface()

実装 - その 6

(AspectJ 1.1.1 OK)

段階 - その 1

public class Context {
	public void contextInterface() {
		System.out.println("Context.contextInterface()");
	}
}
public aspect StrategyAspect pertarget( target(ContextRole) ) {

	private boolean isActive;

	public void activate() {
		this.isActive = true;
	}

	private static aspect Strategy {
		void around(Context context) :
			target(context) && call( void Context.contextInterface() )
		{

			if (StrategyAspect.aspectOf(context).isActive) {
				StrategyAspect.aspectOf(context).algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	private void algorithmInterface() {
		System.out.println("StrategyAspect.algorithmInterface()");
	}

	private static class ContextRole { }

	private static aspect Impl {
		
		private Map roleMap = new HashMap();
		
		StrategyAspect around(Object context) :
			args(context) && call(StrategyAspect StrategyAspect.aspectOf(Object))
		{
		
			Object role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = roleMap.get(context);
			}

			return proceed(role);
		}
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();


		StrategyAspect.aspectOf(context).activate();

		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
StrategyAspect.algorithmInterface()

段階 - その 2 : 複数の Strategy

まずは、コードの重複の削除から・・・
public abstract aspect AbstractStrategyAspect pertarget( target(ContextRole) ) {

	private boolean isActive;

	public void activate() {
		this.isActive = true;
	}

	protected boolean isActive() {
		return isActive;
	}

	protected static class ContextRole { }

	private static aspect Impl {
		
		private Map roleMap = new HashMap();
		
		Object around(Object context) :
			args(context) &&
			call(AbstractStrategyAspect+ AbstractStrategyAspect+.aspectOf(Object) )
		{
			Object role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = roleMap.get(context);
			}

			return proceed(role);
		}
	}
}
public aspect StrategyAspectA extends AbstractStrategyAspect {

	private static aspect Strategy {
		void around(Context context) :
			target(context) && call( void Context.contextInterface() )
		{

			if ( StrategyAspectA.aspectOf(context).isActive() ) {
				StrategyAspectA.aspectOf(context).algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	private void algorithmInterface() {
		System.out.println("StrategyAspectA.algorithmInterface()");
	}
}
public aspect StrategyAspectB extends AbstractStrategyAspect {

	private static aspect Strategy {
		void around(Context context) :
			target(context) && call( void Context.contextInterface() )
		{

			if ( StrategyAspectB.aspectOf(context).isActive() ) {
				StrategyAspectB.aspectOf(context).algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	private void algorithmInterface() {
		System.out.println("StrategyAspectB.algorithmInterface()");
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();


		StrategyAspectA.aspectOf(context).activate();

		context.contextInterface();


		System.out.println();


		context = new Context();
		context.contextInterface();


		StrategyAspectB.aspectOf(context).activate();

		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
StrategyAspectA.algorithmInterface()

Context.contextInterface()
StrategyAspectB.algorithmInterface()

段階 - その 3 : 複数の Strategy - その 2

public abstract aspect AbstractStrategyAspect pertarget( target(ContextRole) ) {

	private boolean isActive;

	private ContextRole role;

	public void activate() {
		this.isActive = true;
		if (role.strategy != null) {
			role.strategy.deactivate();
		}
		role.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	protected boolean isActive() {
		return isActive;
	}

	protected abstract void algorithmInterface();

	private static ContextRole getContextRole(Object context) {
		return (ContextRole)Impl.aspectOf().roleMap.get(context);
	}

	after(ContextRole role): target(role) && initialization( ContextRole.new() ) {
		this.role = role;
	}

	private static aspect Strategy {

		void around(Object context) :
			target(context) &&
			call( void Context.contextInterface() ) &&
			if (getContextRole(context) != null)
		{
			AbstractStrategyAspect strategy = getContextRole(context).strategy;

			if ( strategy != null && strategy.isActive() ) {
				strategy.algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	protected static class ContextRole {
		private AbstractStrategyAspect strategy;
	}

	private static aspect Impl {

		private Map roleMap = new HashMap();
		
		Object around(Object context) :
			args(context) &&
			call(AbstractStrategyAspect+ AbstractStrategyAspect+.aspectOf(Object) )
		{
			Object role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = roleMap.get(context);
			}

			return proceed(role);
		}
	}
}
public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void algorithmInterface() {
		System.out.println("StrategyAspectA.algorithmInterface()");
	}
}
public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void algorithmInterface() {
		System.out.println("StrategyAspectB.algorithmInterface()");
	}
}
public class Client {

	public static void main(String[] args) {

		Context context1 = new Context();
		context1.contextInterface();


		StrategyAspectA.aspectOf(context1).activate();

		context1.contextInterface();


		StrategyAspectB.aspectOf(context1).activate();

		context1.contextInterface();


		System.out.println();


		Context context2 = new Context();
		context2.contextInterface();


		StrategyAspectB.aspectOf(context2).activate();

		context2.contextInterface();


		StrategyAspectA.aspectOf(context2).activate();

		context2.contextInterface();
	}
}
実行結果:
Context.contextInterface()
StrategyAspectA.algorithmInterface()
StrategyAspectB.algorithmInterface()

Context.contextInterface()
StrategyAspectB.algorithmInterface()
StrategyAspectA.algorithmInterface()

段階 - その 4 : 再利用可能にできるか?

public abstract aspect StrategyAspectProtocol pertarget( target(ContextRole) ) {

	private boolean isActive;

	private ContextRole role;

	public void activate() {
		this.isActive = true;
		if (role.strategy != null) {
			role.strategy.deactivate();
		}
		role.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	protected boolean isActive() {
		return isActive;
	}

	protected abstract void algorithmInterface();

	private static ContextRole getContextRole(Object context) {
		return (ContextRole)Impl.aspectOf().roleMap.get(context);
	}

	after(ContextRole role): target(role) && initialization( ContextRole.new() ) {
		this.role = role;
	}

	protected static abstract aspect Strategy {

		protected abstract pointcut callContextInterface();

		void around(Object context) :
			target(context) &&
			callContextInterface() &&
			if (getContextRole(context) != null)
		{
			StrategyAspectProtocol strategy = getContextRole(context).strategy;

			if ( strategy != null && strategy.isActive() ) {
				strategy.algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	protected static class ContextRole {
		private StrategyAspectProtocol strategy;
	}

	private static aspect Impl {

		private Map roleMap = new HashMap();
		
		Object around(Object context) :
			args(context) &&
			call(StrategyAspectProtocol+ StrategyAspectProtocol+.aspectOf(Object) )
		{
			Object role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = roleMap.get(context);
			}

			return proceed(role);
		}
	}
}
public abstract aspect AbstractStrategyAspect extends StrategyAspectProtocol {

	protected void algorithmInterface() {
		myAlgorithmInterface();
	}
	
	protected abstract void myAlgorithmInterface();

	protected static aspect StrategyImpl extends Strategy {

		protected pointcut callContextInterface() :
			call( void Context.contextInterface() );
	}
}
public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectA.myAlgorithmInterface()");
	}
}
public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectB.myAlgorithmInterface()");
	}
}
public class Client {

	public static void main(String[] args) {

		Context context1 = new Context();
		context1.contextInterface();


		StrategyAspectA.aspectOf(context1).activate();

		context1.contextInterface();


		StrategyAspectB.aspectOf(context1).activate();

		context1.contextInterface();


		System.out.println();


		Context context2 = new Context();
		context2.contextInterface();


		StrategyAspectB.aspectOf(context2).activate();

		context2.contextInterface();


		StrategyAspectA.aspectOf(context2).activate();

		context2.contextInterface();
	}
}
実行結果:
Context.contextInterface()
StrategyAspectA.myAlgorithmInterface()
StrategyAspectB.myAlgorithmInterface()

Context.contextInterface()
StrategyAspectB.myAlgorithmInterface()
StrategyAspectA.myAlgorithmInterface()

使用例

ライブラリ側:
package lib;

public class MyContext {
	public void contextInterface() {
		System.out.println("MyContext.contextInterface()");
	}
}
アプリケーション側:
package app;

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

import lib.MyContext;

public abstract aspect AbstractStrategyAspect pertarget( target(ContextRole) ) {

	private boolean isActive;

	private ContextRole role;

	public void activate() {
		this.isActive = true;
		if (role.strategy != null) {
			role.strategy.deactivate();
		}
		role.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	protected boolean isActive() {
		return isActive;
	}

	protected abstract void algorithmInterface();

	private static ContextRole getContextRole(Object context) {
		return (ContextRole)Impl.aspectOf().roleMap.get(context);
	}

	after(ContextRole role): target(role) && initialization( ContextRole.new() ) {
		this.role = role;
	}

	private static aspect Strategy {

		void around(Object context) :
			target(context) &&
			call( void MyContext.contextInterface() ) &&
			if (getContextRole(context) != null)
		{
			AbstractStrategyAspect strategy = getContextRole(context).strategy;

			if ( strategy != null && strategy.isActive() ) {
				strategy.algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	protected static class ContextRole {
		private AbstractStrategyAspect strategy;
	}

	private static aspect Impl {

		private Map roleMap = new HashMap();
		
		Object around(Object context) :
			args(context) &&
			call(AbstractStrategyAspect+ AbstractStrategyAspect+.aspectOf(Object) )
		{
			Object role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = roleMap.get(context);
			}

			return proceed(role);
		}
	}
}
package app;

public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void algorithmInterface() {
		System.out.println("StrategyAspectA.algorithmInterface()");
	}
}
package app;

public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void algorithmInterface() {
		System.out.println("StrategyAspectB.algorithmInterface()");
	}
}
package app;

import lib.MyContext;

public class Client {

	public static void main(String[] args) {

		MyContext context1 = new MyContext();
		context1.contextInterface(); // "MyContext.contextInterface()"


		StrategyAspectA.aspectOf(context1).activate();

		context1.contextInterface(); // "StrategyAspectA.algorithmInterface()"


		StrategyAspectB.aspectOf(context1).activate();

		context1.contextInterface(); // "StrategyAspectB.algorithmInterface()"


		System.out.println();


		MyContext context2 = new MyContext();
		context2.contextInterface(); // "MyContext.contextInterface()"


		StrategyAspectB.aspectOf(context2).activate();

		context2.contextInterface(); // "StrategyAspectB.algorithmInterface()"


		StrategyAspectA.aspectOf(context2).activate();

		context2.contextInterface(); // "StrategyAspectA.algorithmInterface()"
	}
}
実行結果:
MyContext.contextInterface()
StrategyAspectA.algorithmInterface()
StrategyAspectB.algorithmInterface()

MyContext.contextInterface()
StrategyAspectB.algorithmInterface()
StrategyAspectA.algorithmInterface()

使用例 - その 2

ライブラリ側:
package lib;

public class MyContext {
	public void contextInterface() {
		System.out.println("MyContext.contextInterface()");
	}
}
ライブラリ拡張側:
package lib2;

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

public abstract aspect StrategyAspectProtocol pertarget( target(ContextRole) ) {

	private boolean isActive;

	private ContextRole role;

	public void activate() {
		this.isActive = true;
		if (role.strategy != null) {
			role.strategy.deactivate();
		}
		role.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	protected boolean isActive() {
		return isActive;
	}

	protected abstract void algorithmInterface();

	private static ContextRole getContextRole(Object context) {
		return (ContextRole)Impl.aspectOf().roleMap.get(context);
	}

	after(ContextRole role): target(role) && initialization( ContextRole.new() ) {
		this.role = role;
	}

	protected static abstract aspect Strategy {

		protected abstract pointcut callContextInterface();

		void around(Object context) :
			target(context) &&
			callContextInterface() &&
			if (getContextRole(context) != null)
		{
			StrategyAspectProtocol strategy = getContextRole(context).strategy;

			if ( strategy != null && strategy.isActive() ) {
				strategy.algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	protected static class ContextRole {
		private StrategyAspectProtocol strategy;
	}

	private static aspect Impl {

		private Map roleMap = new HashMap();
		
		Object around(Object context) :
			args(context) &&
			call(StrategyAspectProtocol+ StrategyAspectProtocol+.aspectOf(Object) )
		{
			Object role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = roleMap.get(context);
			}

			return proceed(role);
		}
	}
}
package lib2;

import lib.MyContext;

public abstract aspect AbstractStrategyAspect extends StrategyAspectProtocol {

	protected void algorithmInterface() {
		myAlgorithmInterface();
	}
	
	protected abstract void myAlgorithmInterface();

	protected static aspect StrategyImpl extends Strategy {

		protected pointcut callContextInterface() :
			call( void MyContext.contextInterface() );
	}
}
拡張されたライブラリを使うアプリケーション側:
package app;

import lib2.AbstractStrategyAspect;

public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectA.myAlgorithmInterface()");
	}
}
package app;

import lib2.AbstractStrategyAspect;

public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectB.myAlgorithmInterface()");
	}
}
package app;

import lib.MyContext;

public class Client {

	public static void main(String[] args) {

		MyContext context1 = new MyContext();
		context1.contextInterface();


		StrategyAspectA.aspectOf(context1).activate();

		context1.contextInterface();


		StrategyAspectB.aspectOf(context1).activate();

		context1.contextInterface();


		System.out.println();


		MyContext context2 = new MyContext();
		context2.contextInterface();


		StrategyAspectB.aspectOf(context2).activate();

		context2.contextInterface();


		StrategyAspectA.aspectOf(context2).activate();

		context2.contextInterface();
	}
}
実行結果:
MyContext.contextInterface()
StrategyAspectA.myAlgorithmInterface()
StrategyAspectB.myAlgorithmInterface()

MyContext.contextInterface()
StrategyAspectB.myAlgorithmInterface()
StrategyAspectA.myAlgorithmInterface()

実装 - その 7 : PerTargetAspect を使って

(AspectJ 1.1.1 OK, PerTargetAspect)

使用例

ライブラリ(とそのライブラリ拡張ライブラリ)に対してのバイトコード・ウィービングが必要ないことに注目。

ライブラリ側:

package lib;

public class MyContext {
	public void contextInterface() {
		System.out.println("MyContext.contextInterface()");
	}
}
ライブラリ拡張側:
package lib2;

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

import peraspect.PerTarget;
import peraspect.PerTargetAspect;

public abstract aspect StrategyAspectProtocol implements PerTarget {

	private boolean isActive;

	private ContextRole role;

	public void activate() {

		this.isActive = true;
		if (role.strategy != null && role.strategy != this) {
			role.strategy.deactivate();
		}
		role.strategy = this;
	}

	public void deactivate() {
		this.isActive = false;
	}

	protected boolean isActive() {
		return isActive;
	}

	protected abstract void algorithmInterface();

	private static ContextRole getContextRole(Object context) {
		return (ContextRole)Impl.aspectOf().roleMap.get(context);
	}

	protected static abstract aspect Strategy {

		protected abstract pointcut callContextInterface();

		void around(Object context) :
			target(context) &&
			callContextInterface() &&
			if (getContextRole(context) != null)
		{
			StrategyAspectProtocol strategy = getContextRole(context).strategy;

			if ( strategy != null && strategy.isActive() ) {
				strategy.algorithmInterface();

			} else {
				proceed(context);
			}
		}
	}

	public static class ContextRole {
		private StrategyAspectProtocol strategy;
	}

	private static aspect Impl {

		private Map roleMap = new HashMap();
		
		Object around(Object context) :
			args(context) &&
			call(StrategyAspectProtocol+ StrategyAspectProtocol+.aspectOf(Object) )
		{

			ContextRole role = null;
			if ( roleMap.containsKey(context) == false) {
				role = new ContextRole();
				roleMap.put(context, role);

			} else {
				role = (ContextRole)roleMap.get(context);
			}

			StrategyAspectProtocol strategy = (StrategyAspectProtocol)proceed(role);
			if (strategy.role == null) {
				strategy.role = role;
			}

			return strategy;
		}
	}

	protected static abstract aspect AbstractPerTargetImpl extends PerTargetAspect.Impl {

		protected pointcut targetObj(Object targetObj) :
			target(targetObj) && target(ContextRole);
	}
}
package lib2;

import lib.MyContext;

public abstract aspect AbstractStrategyAspect extends StrategyAspectProtocol {

	protected void algorithmInterface() {
		myAlgorithmInterface();
	}
	
	protected abstract void myAlgorithmInterface();

	protected static aspect StrategyImpl extends Strategy {

		protected pointcut callContextInterface() :
			call( void MyContext.contextInterface() );
	}

	protected static abstract aspect AbstractPerTarget extends AbstractPerTargetImpl {
	}

}
拡張されたライブラリを使うクライアント側:
package app;

import lib2.AbstractStrategyAspect;
import peraspect.PerTarget;

public aspect StrategyAspectA extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectA.myAlgorithmInterface() - " + this);
	}

	protected static aspect PerTargetImpl extends AbstractPerTarget {
		protected PerTarget getAspect() {
			return StrategyAspectA.aspectOf();
		}
	}

	public static StrategyAspectA aspectOf(Object obj) {
		return (StrategyAspectA)StrategyAspectA.aspectOf().getAspect(obj);
	}
}
package app;

import lib2.AbstractStrategyAspect;
import peraspect.PerTarget;

public aspect StrategyAspectB extends AbstractStrategyAspect {

	protected void myAlgorithmInterface() {
		System.out.println("StrategyAspectB.myAlgorithmInterface() - " + this);
	}

	protected static aspect PerTargetImpl extends AbstractPerTarget {
		protected PerTarget getAspect() {
			return StrategyAspectB.aspectOf();
		}
	}

	public static StrategyAspectB aspectOf(Object obj) {
		return (StrategyAspectB)StrategyAspectB.aspectOf().getAspect(obj);
	}

}
package app;

import lib.MyContext;

public class Client {

	public static void main(String[] args) {

		MyContext context1 = new MyContext();
		context1.contextInterface();


		StrategyAspectA.aspectOf(context1).activate();

		context1.contextInterface();


		StrategyAspectB.aspectOf(context1).activate();

		context1.contextInterface();


		System.out.println();

		MyContext context2 = new MyContext();
		context2.contextInterface();


		StrategyAspectB.aspectOf(context2).activate();

		context2.contextInterface();


		StrategyAspectA.aspectOf(context2).activate();

		context2.contextInterface();
	}
}
実行結果:
MyContext.contextInterface()
StrategyAspectA.myAlgorithmInterface() - app.StrategyAspectA@e48e1b
StrategyAspectB.myAlgorithmInterface() - app.StrategyAspectB@12dacd1

MyContext.contextInterface()
StrategyAspectB.myAlgorithmInterface() - app.StrategyAspectB@42719c
StrategyAspectA.myAlgorithmInterface() - app.StrategyAspectA@30c221

実装 - その 8

(AspectJ 1.1.1 OK)

段階 - その 1

public class Context {

	public void contextInterface() {
		System.out.println("Context.contextInterface()");
	}
}
public interface Strategy {
	public void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyA.algorithmInterface()");
	}
}
public class ConcreteStrategyB implements Strategy {

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyB.algorithmInterface()");
	}
}
public aspect StrategyAspect pertarget( target(Context) ) {

	private Strategy strategy;

	void around() : call( void Context.contextInterface() ) {

		if (strategy != null) {
			strategy.algorithmInterface();

		} else {
			proceed();
		}
	}
	
	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}
}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();


		StrategyAspect.aspectOf(context).setStrategy( new ConcreteStrategyA() );
		context.contextInterface();


		StrategyAspect.aspectOf(context).setStrategy( new ConcreteStrategyB() );
		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
ConcreteStrategyA.algorithmInterface()
ConcreteStrategyB.algorithmInterface()

段階 - その 2

「実装 7」で見たように pertarget を用いたときの残念な点は、アスペクトと関連付けられるクラスのバイトコードが利用可能でなければならないことです。そのため、pertarget の代わりに PerTargetAspect を代用します。
public class Context { // 変更なし

	public void contextInterface() {
		System.out.println("Context.contextInterface()");
	}
}
public interface Strategy { // 変更なし
	public void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy { // 変更なし

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyA.algorithmInterface()");
	}
}
public class ConcreteStrategyB implements Strategy { // 変更なし

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyB.algorithmInterface()");
	}
}
import peraspect.PerTarget;
import peraspect.PerTargetAspect;

public aspect StrategyAspect implements PerTarget {

	private Strategy strategy;

	void around() : call( void Context.contextInterface() ) {

		if (strategy != null) {
			strategy.algorithmInterface();

		} else {
			proceed();
		}
	}
	
	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}

	protected static aspect PerTargetImpl extends PerTargetAspect.Impl {

		protected pointcut targetObj(Object targetObj) :
			target(targetObj) && target(Context);

		protected PerTarget getAspect() {
			return StrategyAspect.aspectOf();
		}
	}
	
	public static StrategyAspect aspectOf(Object obj) {
		return (StrategyAspect)StrategyAspect.aspectOf().getAspect(obj);
	}

}
public class Client {

	public static void main(String[] args) {

		Context context = new Context();
		context.contextInterface();


		StrategyAspect.aspectOf(context).setStrategy( new ConcreteStrategyA() );
		context.contextInterface();


		StrategyAspect.aspectOf(context).setStrategy( new ConcreteStrategyB() );
		context.contextInterface();
	}
}
実行結果:
Context.contextInterface()
ConcreteStrategyA.algorithmInterface()
ConcreteStrategyB.algorithmInterface()

実装 - その 9

(AspectJ 1.2rc2 OK)

この実装では、Context の役割を持つ対象として、クラスではなくアスペクトを考えます。

public interface Strategy {

	public void algorithmInterface();
}

public class ConcreteStrategyA implements Strategy {

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyA.algorithmInterface()");
	}
}
public class ConcreteStrategyB implements Strategy {

	public void algorithmInterface() {
		System.out.println("ConcreteStrategyB.algorithmInterface()");
	}
}
public class Component {

	public void method() {
		System.out.println("Component.method()");
	}
}
public aspect ComponentAspect {

	private Strategy strategy = new NullStrategy();

	before() : call( void Component.method() ) {
		strategy.algorithmInterface();
	}

	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}
	
	private static class NullStrategy implements Strategy {
		public void algorithmInterface() { }
	}
}
public class Client {

	public static void main(String[] args) {

		Component comp = new Component();

		comp.method();

		System.out.println();


		ComponentAspect.aspectOf().setStrategy( new ConcreteStrategyA() );

		comp.method();

		System.out.println();


		ComponentAspect.aspectOf().setStrategy( new ConcreteStrategyB() );

		comp.method();

		System.out.println();

	}
}
実行結果:
Component.method()

ConcreteStrategyA.algorithmInterface()
Component.method()

ConcreteStrategyB.algorithmInterface()
Component.method()

使用例 - その 1

(AspectJ 1.2rc2 OK)

import org.aspectj.lang.JoinPoint;

public interface LoggingStrategy {

	public void log(JoinPoint jp);
}
import org.aspectj.lang.JoinPoint;

public class SimpleLogging implements LoggingStrategy {

	public void log(JoinPoint jp) {
		System.out.println("SimpleLogging");
	}
}
import org.aspectj.lang.JoinPoint;

public class NormalLogging implements LoggingStrategy {

	public void log(JoinPoint jp) {
		System.out.println("NormalLogging"); // jp 使ってロギングとか
	}
}
import org.aspectj.lang.JoinPoint;

public aspect LoggingAspect {

	private LoggingStrategy strategy = new NullLoggingStrategy();

	before() : call( void Component.method() ) {
		strategy.log(thisJoinPoint);
	}

	public void setLoggingStrategy(LoggingStrategy strategy) {
		this.strategy = strategy;
	}
	
	private static class NullLoggingStrategy implements LoggingStrategy {
		public void log(JoinPoint jp) { }
	}
}
public class Client {

	public static void main(String[] args) {

		Component comp = new Component();

		comp.method();

		System.out.println();


		LoggingAspect.aspectOf().setLoggingStrategy( new SimpleLogging() );

		comp.method();

		System.out.println();


		LoggingAspect.aspectOf().setLoggingStrategy( new NormalLogging() );

		comp.method();

		System.out.println();

	}
}
実行結果:
Component.method()

SimpleLogging
Component.method()

NormalLogging
Component.method()

参考文献とリソース

参考文献: リソース:

更新履歴

todo